PageRenderTime 74ms CodeModel.GetById 35ms RepoModel.GetById 0ms app.codeStats 1ms

/trunk/managed/Mono.Addins.Lite/Mono.Addins/Mono.Addins.Database/AddinDatabase.cs

https://bitbucket.org/KyanhaLLC/opensim-libs
C# | 1964 lines | 1550 code | 291 blank | 123 comment | 400 complexity | 1d0882a95da15096cbb471b4e81baf8b MD5 | raw file
Possible License(s): Apache-2.0, BSD-2-Clause, MIT, LGPL-2.1, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, GPL-3.0, BSD-3-Clause

Large files files are truncated, but you can click here to view the full file

  1. //
  2. // AddinDatabase.cs
  3. //
  4. // Author:
  5. // Lluis Sanchez Gual
  6. //
  7. // Copyright (C) 2007 Novell, Inc (http://www.novell.com)
  8. //
  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:
  16. //
  17. // The above copyright notice and this permission notice shall be
  18. // included in all copies or substantial portions of the Software.
  19. //
  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.
  27. //
  28. using System;
  29. using System.Threading;
  30. using System.Collections;
  31. using System.Collections.Specialized;
  32. using System.IO;
  33. using System.Xml;
  34. using System.Reflection;
  35. using Mono.Addins.Description;
  36. using System.Collections.Generic;
  37. using System.Linq;
  38. namespace Mono.Addins.Database
  39. {
  40. class AddinDatabase
  41. {
  42. public const string GlobalDomain = "global";
  43. public const string UnknownDomain = "unknown";
  44. public const string VersionTag = "001";
  45. List<Addin> allSetupInfos;
  46. List<Addin> addinSetupInfos;
  47. List<Addin> rootSetupInfos;
  48. internal static bool RunningSetupProcess;
  49. bool fatalDatabseError;
  50. Hashtable cachedAddinSetupInfos = new Hashtable ();
  51. AddinScanResult currentScanResult;
  52. AddinHostIndex hostIndex;
  53. FileDatabase fileDatabase;
  54. string addinDbDir;
  55. DatabaseConfiguration config = null;
  56. AddinRegistry registry;
  57. int lastDomainId;
  58. AddinEngine addinEngine;
  59. AddinFileSystemExtension fs = new AddinFileSystemExtension ();
  60. List<object> extensions = new List<object> ();
  61. public AddinDatabase (AddinEngine addinEngine, AddinRegistry registry)
  62. {
  63. this.addinEngine = addinEngine;
  64. this.registry = registry;
  65. addinDbDir = Path.Combine (registry.AddinCachePath, "addin-db-" + VersionTag);
  66. fileDatabase = new FileDatabase (AddinDbDir);
  67. }
  68. string AddinDbDir {
  69. get { return addinDbDir; }
  70. }
  71. public AddinFileSystemExtension FileSystem {
  72. get { return fs; }
  73. }
  74. public string AddinCachePath {
  75. get { return Path.Combine (AddinDbDir, "addin-data"); }
  76. }
  77. public string AddinFolderCachePath {
  78. get { return Path.Combine (AddinDbDir, "addin-dir-data"); }
  79. }
  80. public string AddinPrivateDataPath {
  81. get { return Path.Combine (AddinDbDir, "addin-priv-data"); }
  82. }
  83. public string HostsPath {
  84. get { return Path.Combine (AddinDbDir, "hosts"); }
  85. }
  86. string HostIndexFile {
  87. get { return Path.Combine (AddinDbDir, "host-index"); }
  88. }
  89. string ConfigFile {
  90. get { return Path.Combine (AddinDbDir, "config.xml"); }
  91. }
  92. internal bool IsGlobalRegistry {
  93. get {
  94. return registry.RegistryPath == AddinRegistry.GlobalRegistryPath;
  95. }
  96. }
  97. public AddinRegistry Registry {
  98. get {
  99. return this.registry;
  100. }
  101. }
  102. public void Clear ()
  103. {
  104. if (Directory.Exists (AddinCachePath))
  105. Directory.Delete (AddinCachePath, true);
  106. if (Directory.Exists (AddinFolderCachePath))
  107. Directory.Delete (AddinFolderCachePath, true);
  108. }
  109. public void CopyExtensions (AddinDatabase other)
  110. {
  111. foreach (object o in other.extensions)
  112. RegisterExtension (o);
  113. }
  114. public void RegisterExtension (object extension)
  115. {
  116. extensions.Add (extension);
  117. if (extension is AddinFileSystemExtension)
  118. fs = (AddinFileSystemExtension) extension;
  119. else
  120. throw new NotSupportedException ();
  121. }
  122. public void UnregisterExtension (object extension)
  123. {
  124. extensions.Remove (extension);
  125. if ((extension as AddinFileSystemExtension) == fs)
  126. fs = new AddinFileSystemExtension ();
  127. else
  128. throw new InvalidOperationException ();
  129. }
  130. public ExtensionNodeSet FindNodeSet (string domain, string addinId, string id)
  131. {
  132. return FindNodeSet (domain, addinId, id, new Hashtable ());
  133. }
  134. ExtensionNodeSet FindNodeSet (string domain, string addinId, string id, Hashtable visited)
  135. {
  136. if (visited.Contains (addinId))
  137. return null;
  138. visited.Add (addinId, addinId);
  139. Addin addin = GetInstalledAddin (domain, addinId, true, false);
  140. if (addin == null)
  141. return null;
  142. AddinDescription desc = addin.Description;
  143. if (desc == null)
  144. return null;
  145. foreach (ExtensionNodeSet nset in desc.ExtensionNodeSets)
  146. if (nset.Id == id)
  147. return nset;
  148. // Not found in the add-in. Look on add-ins on which it depends
  149. foreach (Dependency dep in desc.MainModule.Dependencies) {
  150. AddinDependency adep = dep as AddinDependency;
  151. if (adep == null) continue;
  152. string aid = Addin.GetFullId (desc.Namespace, adep.AddinId, adep.Version);
  153. ExtensionNodeSet nset = FindNodeSet (domain, aid, id, visited);
  154. if (nset != null)
  155. return nset;
  156. }
  157. return null;
  158. }
  159. public IEnumerable<Addin> GetInstalledAddins (string domain, AddinSearchFlagsInternal flags)
  160. {
  161. if (domain == null)
  162. domain = registry.CurrentDomain;
  163. // Get the cached list if the add-in list has already been loaded.
  164. // The domain doesn't have to be checked again, since it is always the same
  165. IEnumerable<Addin> result = null;
  166. if ((flags & AddinSearchFlagsInternal.IncludeAll) == AddinSearchFlagsInternal.IncludeAll) {
  167. if (allSetupInfos != null)
  168. result = allSetupInfos;
  169. }
  170. else if ((flags & AddinSearchFlagsInternal.IncludeAddins) == AddinSearchFlagsInternal.IncludeAddins) {
  171. if (addinSetupInfos != null)
  172. result = addinSetupInfos;
  173. }
  174. else {
  175. if (rootSetupInfos != null)
  176. result = rootSetupInfos;
  177. }
  178. if (result == null) {
  179. InternalCheck (domain);
  180. using (fileDatabase.LockRead ()) {
  181. result = InternalGetInstalledAddins (domain, null, flags & ~AddinSearchFlagsInternal.LatestVersionsOnly);
  182. }
  183. }
  184. if ((flags & AddinSearchFlagsInternal.LatestVersionsOnly) == AddinSearchFlagsInternal.LatestVersionsOnly)
  185. result = result.Where (a => a.IsLatestVersion);
  186. if ((flags & AddinSearchFlagsInternal.ExcludePendingUninstall) == AddinSearchFlagsInternal.ExcludePendingUninstall)
  187. result = result.Where (a => !IsRegisteredForUninstall (a.Description.Domain, a.Id));
  188. return result;
  189. }
  190. IEnumerable<Addin> InternalGetInstalledAddins (string domain, AddinSearchFlagsInternal type)
  191. {
  192. return InternalGetInstalledAddins (domain, null, type);
  193. }
  194. IEnumerable<Addin> InternalGetInstalledAddins (string domain, string idFilter, AddinSearchFlagsInternal type)
  195. {
  196. if ((type & AddinSearchFlagsInternal.LatestVersionsOnly) != 0)
  197. throw new InvalidOperationException ("LatestVersionsOnly flag not supported here");
  198. if (allSetupInfos == null) {
  199. Dictionary<string,Addin> adict = new Dictionary<string, Addin> ();
  200. // Global add-ins are valid for any private domain
  201. if (domain != AddinDatabase.GlobalDomain)
  202. FindInstalledAddins (adict, AddinDatabase.GlobalDomain, idFilter);
  203. FindInstalledAddins (adict, domain, idFilter);
  204. List<Addin> alist = new List<Addin> (adict.Values);
  205. UpdateLastVersionFlags (alist);
  206. if (idFilter != null)
  207. return alist;
  208. allSetupInfos = alist;
  209. }
  210. if ((type & AddinSearchFlagsInternal.IncludeAll) == AddinSearchFlagsInternal.IncludeAll)
  211. return FilterById (allSetupInfos, idFilter);
  212. if ((type & AddinSearchFlagsInternal.IncludeAddins) == AddinSearchFlagsInternal.IncludeAddins) {
  213. if (addinSetupInfos == null) {
  214. addinSetupInfos = new List<Addin> ();
  215. foreach (Addin adn in allSetupInfos)
  216. if (!adn.Description.IsRoot)
  217. addinSetupInfos.Add (adn);
  218. }
  219. return FilterById (addinSetupInfos, idFilter);
  220. }
  221. else {
  222. if (rootSetupInfos == null) {
  223. rootSetupInfos = new List<Addin> ();
  224. foreach (Addin adn in allSetupInfos)
  225. if (adn.Description.IsRoot)
  226. rootSetupInfos.Add (adn);
  227. }
  228. return FilterById (rootSetupInfos, idFilter);
  229. }
  230. }
  231. IEnumerable<Addin> FilterById (List<Addin> addins, string id)
  232. {
  233. if (id == null)
  234. return addins;
  235. return addins.Where (a => Addin.GetIdName (a.Id) == id);
  236. }
  237. void FindInstalledAddins (Dictionary<string,Addin> result, string domain, string idFilter)
  238. {
  239. if (idFilter == null)
  240. idFilter = "*";
  241. string dir = Path.Combine (AddinCachePath, domain);
  242. if (Directory.Exists (dir)) {
  243. foreach (string file in fileDatabase.GetDirectoryFiles (dir, idFilter + ",*.maddin")) {
  244. string id = Path.GetFileNameWithoutExtension (file);
  245. if (!result.ContainsKey (id)) {
  246. var adesc = GetInstalledDomainAddin (domain, id, true, false, false);
  247. if (adesc != null)
  248. result.Add (id, adesc);
  249. }
  250. }
  251. }
  252. }
  253. void UpdateLastVersionFlags (List<Addin> addins)
  254. {
  255. Dictionary<string,string> versions = new Dictionary<string, string> ();
  256. foreach (Addin a in addins) {
  257. string last;
  258. string id, version;
  259. Addin.GetIdParts (a.Id, out id, out version);
  260. if (!versions.TryGetValue (id, out last) || Addin.CompareVersions (last, version) > 0)
  261. versions [id] = version;
  262. }
  263. foreach (Addin a in addins) {
  264. string id, version;
  265. Addin.GetIdParts (a.Id, out id, out version);
  266. a.IsLatestVersion = versions [id] == version;
  267. }
  268. }
  269. public Addin GetInstalledAddin (string domain, string id)
  270. {
  271. return GetInstalledAddin (domain, id, false, false);
  272. }
  273. public Addin GetInstalledAddin (string domain, string id, bool exactVersionMatch)
  274. {
  275. return GetInstalledAddin (domain, id, exactVersionMatch, false);
  276. }
  277. public Addin GetInstalledAddin (string domain, string id, bool exactVersionMatch, bool enabledOnly)
  278. {
  279. // Try the given domain, and if not found, try the shared domain
  280. Addin ad = GetInstalledDomainAddin (domain, id, exactVersionMatch, enabledOnly, true);
  281. if (ad != null)
  282. return ad;
  283. if (domain != AddinDatabase.GlobalDomain)
  284. return GetInstalledDomainAddin (AddinDatabase.GlobalDomain, id, exactVersionMatch, enabledOnly, true);
  285. else
  286. return null;
  287. }
  288. Addin GetInstalledDomainAddin (string domain, string id, bool exactVersionMatch, bool enabledOnly, bool dbLockCheck)
  289. {
  290. Addin sinfo = null;
  291. string idd = id + " " + domain;
  292. object ob = cachedAddinSetupInfos [idd];
  293. if (ob != null) {
  294. sinfo = ob as Addin;
  295. if (sinfo != null) {
  296. if (!enabledOnly || sinfo.Enabled)
  297. return sinfo;
  298. if (exactVersionMatch)
  299. return null;
  300. }
  301. else if (enabledOnly)
  302. // Ignore the 'not installed' flag when disabled add-ins are allowed
  303. return null;
  304. }
  305. if (dbLockCheck)
  306. InternalCheck (domain);
  307. using ((dbLockCheck ? fileDatabase.LockRead () : null))
  308. {
  309. string path = GetDescriptionPath (domain, id);
  310. if (sinfo == null && fileDatabase.Exists (path)) {
  311. sinfo = new Addin (this, path);
  312. cachedAddinSetupInfos [idd] = sinfo;
  313. if (!enabledOnly || sinfo.Enabled)
  314. return sinfo;
  315. if (exactVersionMatch) {
  316. // Cache lookups with negative result
  317. cachedAddinSetupInfos [idd] = this;
  318. return null;
  319. }
  320. }
  321. // Exact version not found. Look for a compatible version
  322. if (!exactVersionMatch) {
  323. sinfo = null;
  324. string version, name, bestVersion = null;
  325. Addin.GetIdParts (id, out name, out version);
  326. foreach (Addin ia in InternalGetInstalledAddins (domain, name, AddinSearchFlagsInternal.IncludeAll))
  327. {
  328. if ((!enabledOnly || ia.Enabled) &&
  329. (version.Length == 0 || ia.SupportsVersion (version)) &&
  330. (bestVersion == null || Addin.CompareVersions (bestVersion, ia.Version) > 0))
  331. {
  332. bestVersion = ia.Version;
  333. sinfo = ia;
  334. }
  335. }
  336. if (sinfo != null) {
  337. cachedAddinSetupInfos [idd] = sinfo;
  338. return sinfo;
  339. }
  340. }
  341. // Cache lookups with negative result
  342. // Ignore the 'not installed' flag when disabled add-ins are allowed
  343. if (enabledOnly)
  344. cachedAddinSetupInfos [idd] = this;
  345. return null;
  346. }
  347. }
  348. public void Shutdown ()
  349. {
  350. ResetCachedData ();
  351. }
  352. public Addin GetAddinForHostAssembly (string domain, string assemblyLocation)
  353. {
  354. InternalCheck (domain);
  355. Addin ainfo = null;
  356. object ob = cachedAddinSetupInfos [assemblyLocation];
  357. if (ob != null)
  358. return ob as Addin; // Don't use a cast here is ob may not be an Addin.
  359. AddinHostIndex index = GetAddinHostIndex ();
  360. string addin, addinFile, rdomain;
  361. if (index.GetAddinForAssembly (assemblyLocation, out addin, out addinFile, out rdomain)) {
  362. string sid = addin + " " + rdomain;
  363. ainfo = cachedAddinSetupInfos [sid] as Addin;
  364. if (ainfo == null)
  365. ainfo = new Addin (this, GetDescriptionPath (rdomain, addin));
  366. cachedAddinSetupInfos [assemblyLocation] = ainfo;
  367. cachedAddinSetupInfos [addin + " " + rdomain] = ainfo;
  368. }
  369. return ainfo;
  370. }
  371. public bool IsAddinEnabled (string domain, string id)
  372. {
  373. Addin ainfo = GetInstalledAddin (domain, id);
  374. if (ainfo != null)
  375. return ainfo.Enabled;
  376. else
  377. return false;
  378. }
  379. internal bool IsAddinEnabled (string domain, string id, bool exactVersionMatch)
  380. {
  381. if (!exactVersionMatch)
  382. return IsAddinEnabled (domain, id);
  383. Addin ainfo = GetInstalledAddin (domain, id, exactVersionMatch, false);
  384. if (ainfo == null)
  385. return false;
  386. return Configuration.IsEnabled (id, ainfo.AddinInfo.EnabledByDefault);
  387. }
  388. public void EnableAddin (string domain, string id)
  389. {
  390. EnableAddin (domain, id, true);
  391. }
  392. internal void EnableAddin (string domain, string id, bool exactVersionMatch)
  393. {
  394. Addin ainfo = GetInstalledAddin (domain, id, exactVersionMatch, false);
  395. if (ainfo == null)
  396. // It may be an add-in root
  397. return;
  398. if (IsAddinEnabled (domain, id))
  399. return;
  400. // Enable required add-ins
  401. foreach (Dependency dep in ainfo.AddinInfo.Dependencies) {
  402. if (dep is AddinDependency) {
  403. AddinDependency adep = dep as AddinDependency;
  404. string adepid = Addin.GetFullId (ainfo.AddinInfo.Namespace, adep.AddinId, adep.Version);
  405. EnableAddin (domain, adepid, false);
  406. }
  407. }
  408. Configuration.SetStatus (id, true, ainfo.AddinInfo.EnabledByDefault);
  409. SaveConfiguration ();
  410. if (addinEngine != null && addinEngine.IsInitialized)
  411. addinEngine.ActivateAddin (id);
  412. }
  413. public void DisableAddin (string domain, string id)
  414. {
  415. Addin ai = GetInstalledAddin (domain, id, true);
  416. if (ai == null)
  417. throw new InvalidOperationException ("Add-in '" + id + "' not installed.");
  418. if (!IsAddinEnabled (domain, id))
  419. return;
  420. Configuration.SetStatus (id, false, ai.AddinInfo.EnabledByDefault);
  421. SaveConfiguration ();
  422. // Disable all add-ins which depend on it
  423. try {
  424. string idName = Addin.GetIdName (id);
  425. foreach (Addin ainfo in GetInstalledAddins (domain, AddinSearchFlagsInternal.IncludeAddins)) {
  426. foreach (Dependency dep in ainfo.AddinInfo.Dependencies) {
  427. AddinDependency adep = dep as AddinDependency;
  428. if (adep == null)
  429. continue;
  430. string adepid = Addin.GetFullId (ainfo.AddinInfo.Namespace, adep.AddinId, null);
  431. if (adepid != idName)
  432. continue;
  433. // The add-in that has been disabled, might be a requeriment of this one, or maybe not
  434. // if there is an older version available. Check it now.
  435. adepid = Addin.GetFullId (ainfo.AddinInfo.Namespace, adep.AddinId, adep.Version);
  436. Addin adepinfo = GetInstalledAddin (domain, adepid, false, true);
  437. if (adepinfo == null) {
  438. DisableAddin (domain, ainfo.Id);
  439. break;
  440. }
  441. }
  442. }
  443. }
  444. catch {
  445. // If something goes wrong, enable the add-in again
  446. Configuration.SetStatus (id, true, ai.AddinInfo.EnabledByDefault);
  447. SaveConfiguration ();
  448. throw;
  449. }
  450. if (addinEngine != null && addinEngine.IsInitialized)
  451. addinEngine.UnloadAddin (id);
  452. }
  453. public void RegisterForUninstall (string domain, string id, IEnumerable<string> files)
  454. {
  455. DisableAddin (domain, id);
  456. Configuration.RegisterForUninstall (id, files);
  457. SaveConfiguration ();
  458. }
  459. public bool IsRegisteredForUninstall (string domain, string addinId)
  460. {
  461. return Configuration.IsRegisteredForUninstall (addinId);
  462. }
  463. internal bool HasPendingUninstalls (string domain)
  464. {
  465. return Configuration.HasPendingUninstalls;
  466. }
  467. internal string GetDescriptionPath (string domain, string id)
  468. {
  469. return Path.Combine (Path.Combine (AddinCachePath, domain), id + ".maddin");
  470. }
  471. void InternalCheck (string domain)
  472. {
  473. // If the database is broken, don't try to regenerate it at every check.
  474. if (fatalDatabseError)
  475. return;
  476. bool update = false;
  477. using (fileDatabase.LockRead ()) {
  478. if (!Directory.Exists (AddinCachePath)) {
  479. update = true;
  480. }
  481. }
  482. if (update)
  483. Update (null, domain);
  484. }
  485. void GenerateAddinExtensionMapsInternal (IProgressStatus monitor, List<string> addinsToUpdate, List<string> addinsToUpdateRelations, List<string> removedAddins)
  486. {
  487. AddinUpdateData updateData = new AddinUpdateData (this, monitor);
  488. // Clear cached data
  489. cachedAddinSetupInfos.Clear ();
  490. // Collect all information
  491. AddinIndex addinHash = new AddinIndex ();
  492. if (monitor.LogLevel > 1)
  493. monitor.Log ("Generating add-in extension maps");
  494. Hashtable changedAddins = null;
  495. ArrayList descriptionsToSave = new ArrayList ();
  496. ArrayList files = new ArrayList ();
  497. bool partialGeneration = addinsToUpdate != null;
  498. string[] domains = GetDomains ();
  499. // Get the files to be updated
  500. if (partialGeneration) {
  501. changedAddins = new Hashtable ();
  502. if (monitor.LogLevel > 2)
  503. monitor.Log ("Doing a partial registry update.\nAdd-ins to be updated:");
  504. // Get the files and ids of all add-ins that have to be updated
  505. // Include removed add-ins: if there are several instances of the same add-in, removing one of
  506. // them will make other instances to show up. If there is a single instance, its files are
  507. // already removed.
  508. foreach (string sa in addinsToUpdate.Union (removedAddins)) {
  509. changedAddins [sa] = sa;
  510. if (monitor.LogLevel > 2)
  511. monitor.Log (" - " + sa);
  512. foreach (string file in GetAddinFiles (sa, domains)) {
  513. if (!files.Contains (file)) {
  514. files.Add (file);
  515. string an = Path.GetFileNameWithoutExtension (file);
  516. changedAddins [an] = an;
  517. if (monitor.LogLevel > 2 && an != sa)
  518. monitor.Log (" - " + an);
  519. }
  520. }
  521. }
  522. if (monitor.LogLevel > 2)
  523. monitor.Log ("Add-ins whose relations have to be updated:");
  524. // Get the files and ids of all add-ins whose relations have to be updated
  525. foreach (string sa in addinsToUpdateRelations) {
  526. foreach (string file in GetAddinFiles (sa, domains)) {
  527. if (!files.Contains (file)) {
  528. if (monitor.LogLevel > 2) {
  529. string an = Path.GetFileNameWithoutExtension (file);
  530. monitor.Log (" - " + an);
  531. }
  532. files.Add (file);
  533. }
  534. }
  535. }
  536. }
  537. else {
  538. foreach (string dom in domains)
  539. files.AddRange (fileDatabase.GetDirectoryFiles (Path.Combine (AddinCachePath, dom), "*.maddin"));
  540. }
  541. // Load the descriptions.
  542. foreach (string file in files) {
  543. AddinDescription conf;
  544. if (!ReadAddinDescription (monitor, file, out conf)) {
  545. SafeDelete (monitor, file);
  546. continue;
  547. }
  548. // If the original file does not exist, the description can be deleted
  549. if (!fs.FileExists (conf.AddinFile)) {
  550. SafeDelete (monitor, file);
  551. continue;
  552. }
  553. // Remove old data from the description. Remove the data of the add-ins that
  554. // have changed. This data will be re-added later.
  555. conf.UnmergeExternalData (changedAddins);
  556. descriptionsToSave.Add (conf);
  557. addinHash.Add (conf);
  558. }
  559. // Sort the add-ins, to make sure add-ins are processed before
  560. // all their dependencies
  561. var sorted = addinHash.GetSortedAddins ();
  562. // Register extension points and node sets
  563. foreach (AddinDescription conf in sorted)
  564. CollectExtensionPointData (conf, updateData);
  565. if (monitor.LogLevel > 2)
  566. monitor.Log ("Registering new extensions:");
  567. // Register extensions
  568. foreach (AddinDescription conf in sorted) {
  569. if (changedAddins == null || changedAddins.ContainsKey (conf.AddinId)) {
  570. if (monitor.LogLevel > 2)
  571. monitor.Log ("- " + conf.AddinId + " (" + conf.Domain + ")");
  572. CollectExtensionData (monitor, addinHash, conf, updateData);
  573. }
  574. }
  575. // Save the maps
  576. foreach (AddinDescription conf in descriptionsToSave) {
  577. ConsolidateExtensions (conf);
  578. conf.SaveBinary (fileDatabase);
  579. }
  580. if (monitor.LogLevel > 1) {
  581. monitor.Log ("Addin relation map generated.");
  582. monitor.Log (" Addins Updated: " + descriptionsToSave.Count);
  583. monitor.Log (" Extension points: " + updateData.RelExtensionPoints);
  584. monitor.Log (" Extensions: " + updateData.RelExtensions);
  585. monitor.Log (" Extension nodes: " + updateData.RelExtensionNodes);
  586. monitor.Log (" Node sets: " + updateData.RelNodeSetTypes);
  587. }
  588. }
  589. void ConsolidateExtensions (AddinDescription conf)
  590. {
  591. // Merges extensions with the same path
  592. foreach (ModuleDescription module in conf.AllModules) {
  593. Dictionary<string,Extension> extensions = new Dictionary<string, Extension> ();
  594. foreach (Extension ext in module.Extensions) {
  595. Extension mainExt;
  596. if (extensions.TryGetValue (ext.Path, out mainExt)) {
  597. ArrayList list = new ArrayList ();
  598. EnsureInsertionsSorted (ext.ExtensionNodes);
  599. list.AddRange (ext.ExtensionNodes);
  600. int pos = -1;
  601. foreach (ExtensionNodeDescription node in list) {
  602. ext.ExtensionNodes.Remove (node);
  603. AddNodeSorted (mainExt.ExtensionNodes, node, ref pos);
  604. }
  605. } else {
  606. extensions [ext.Path] = ext;
  607. EnsureInsertionsSorted (ext.ExtensionNodes);
  608. }
  609. }
  610. // Sort the nodes
  611. }
  612. }
  613. void EnsureInsertionsSorted (ExtensionNodeDescriptionCollection list)
  614. {
  615. // Makes sure that the nodes in the collections are properly sorted wrt insertafter and insertbefore attributes
  616. Dictionary<string,ExtensionNodeDescription> added = new Dictionary<string, ExtensionNodeDescription> ();
  617. List<ExtensionNodeDescription> halfSorted = new List<ExtensionNodeDescription> ();
  618. bool orderChanged = false;
  619. for (int n = list.Count - 1; n >= 0; n--) {
  620. ExtensionNodeDescription node = list [n];
  621. if (node.Id.Length > 0)
  622. added [node.Id] = node;
  623. if (node.InsertAfter.Length > 0) {
  624. ExtensionNodeDescription relNode;
  625. if (added.TryGetValue (node.InsertAfter, out relNode)) {
  626. // Out of order. Move it before the referenced node
  627. int i = halfSorted.IndexOf (relNode);
  628. halfSorted.Insert (i, node);
  629. orderChanged = true;
  630. } else {
  631. halfSorted.Add (node);
  632. }
  633. } else
  634. halfSorted.Add (node);
  635. }
  636. halfSorted.Reverse ();
  637. List<ExtensionNodeDescription> fullSorted = new List<ExtensionNodeDescription> ();
  638. added.Clear ();
  639. foreach (ExtensionNodeDescription node in halfSorted) {
  640. if (node.Id.Length > 0)
  641. added [node.Id] = node;
  642. if (node.InsertBefore.Length > 0) {
  643. ExtensionNodeDescription relNode;
  644. if (added.TryGetValue (node.InsertBefore, out relNode)) {
  645. // Out of order. Move it before the referenced node
  646. int i = fullSorted.IndexOf (relNode);
  647. fullSorted.Insert (i, node);
  648. orderChanged = true;
  649. } else {
  650. fullSorted.Add (node);
  651. }
  652. } else
  653. fullSorted.Add (node);
  654. }
  655. if (orderChanged) {
  656. list.Clear ();
  657. foreach (ExtensionNodeDescription node in fullSorted)
  658. list.Add (node);
  659. }
  660. }
  661. void AddNodeSorted (ExtensionNodeDescriptionCollection list, ExtensionNodeDescription node, ref int curPos)
  662. {
  663. // Adds the node at the correct position, taking into account insertbefore and insertafter
  664. if (node.InsertAfter.Length > 0) {
  665. string afterId = node.InsertAfter;
  666. for (int n=0; n<list.Count; n++) {
  667. if (list[n].Id == afterId) {
  668. list.Insert (n + 1, node);
  669. curPos = n + 2;
  670. return;
  671. }
  672. }
  673. }
  674. else if (node.InsertBefore.Length > 0) {
  675. string beforeId = node.InsertBefore;
  676. for (int n=0; n<list.Count; n++) {
  677. if (list[n].Id == beforeId) {
  678. list.Insert (n, node);
  679. curPos = n + 1;
  680. return;
  681. }
  682. }
  683. }
  684. if (curPos == -1)
  685. list.Add (node);
  686. else
  687. list.Insert (curPos++, node);
  688. }
  689. IEnumerable GetAddinFiles (string fullId, string[] domains)
  690. {
  691. // Look for all versions of the add-in, because this id may be the id of a reference,
  692. // and the exact reference version may not be installed.
  693. string s = fullId;
  694. int i = s.LastIndexOf (',');
  695. if (i != -1)
  696. s = s.Substring (0, i);
  697. s += ",*";
  698. // Look for the add-in in any of the existing folders
  699. foreach (string domain in domains) {
  700. string mp = GetDescriptionPath (domain, s);
  701. string dir = Path.GetDirectoryName (mp);
  702. string pat = Path.GetFileName (mp);
  703. foreach (string fmp in fileDatabase.GetDirectoryFiles (dir, pat))
  704. yield return fmp;
  705. }
  706. }
  707. // Collects extension data in a hash table. The key is the path, the value is a list
  708. // of add-ins ids that extend that path
  709. void CollectExtensionPointData (AddinDescription conf, AddinUpdateData updateData)
  710. {
  711. foreach (ExtensionNodeSet nset in conf.ExtensionNodeSets) {
  712. try {
  713. updateData.RegisterNodeSet (conf, nset);
  714. updateData.RelNodeSetTypes++;
  715. } catch (Exception ex) {
  716. throw new InvalidOperationException ("Error reading node set: " + nset.Id, ex);
  717. }
  718. }
  719. foreach (ExtensionPoint ep in conf.ExtensionPoints) {
  720. try {
  721. updateData.RegisterExtensionPoint (conf, ep);
  722. updateData.RelExtensionPoints++;
  723. } catch (Exception ex) {
  724. throw new InvalidOperationException ("Error reading extension point: " + ep.Path, ex);
  725. }
  726. }
  727. }
  728. void CollectExtensionData (IProgressStatus monitor, AddinIndex addinHash, AddinDescription conf, AddinUpdateData updateData)
  729. {
  730. IEnumerable<string> missingDeps = addinHash.GetMissingDependencies (conf, conf.MainModule);
  731. if (missingDeps.Any ()) {
  732. string w = "The add-in '" + conf.AddinId + "' could not be updated because some of its dependencies are missing or not compatible:";
  733. w += BuildMissingAddinsList (addinHash, conf, missingDeps);
  734. monitor.ReportWarning (w);
  735. return;
  736. }
  737. CollectModuleExtensionData (conf, conf.MainModule, updateData);
  738. foreach (ModuleDescription module in conf.OptionalModules) {
  739. missingDeps = addinHash.GetMissingDependencies (conf, module);
  740. if (missingDeps.Any ()) {
  741. if (monitor.LogLevel > 1) {
  742. 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:";
  743. w += BuildMissingAddinsList (addinHash, conf, missingDeps);
  744. }
  745. }
  746. else
  747. CollectModuleExtensionData (conf, module, updateData);
  748. }
  749. }
  750. string BuildMissingAddinsList (AddinIndex addinHash, AddinDescription conf, IEnumerable<string> missingDeps)
  751. {
  752. string w = "";
  753. foreach (string dep in missingDeps) {
  754. var found = addinHash.GetSimilarExistingAddin (conf, dep);
  755. if (found == null)
  756. w += "\n missing: " + dep;
  757. else
  758. w += "\n required: " + dep + ", found: " + found.AddinId;
  759. }
  760. return w;
  761. }
  762. void CollectModuleExtensionData (AddinDescription conf, ModuleDescription module, AddinUpdateData updateData)
  763. {
  764. foreach (Extension ext in module.Extensions) {
  765. updateData.RelExtensions++;
  766. updateData.RegisterExtension (conf, module, ext);
  767. AddChildExtensions (conf, module, updateData, ext.Path, ext.ExtensionNodes, false);
  768. }
  769. }
  770. void AddChildExtensions (AddinDescription conf, ModuleDescription module, AddinUpdateData updateData, string path, ExtensionNodeDescriptionCollection nodes, bool conditionChildren)
  771. {
  772. // Don't register conditions as extension nodes.
  773. if (!conditionChildren)
  774. updateData.RegisterExtension (conf, module, path);
  775. foreach (ExtensionNodeDescription node in nodes) {
  776. if (node.NodeName == "ComplexCondition")
  777. continue;
  778. updateData.RelExtensionNodes++;
  779. string id = node.GetAttribute ("id");
  780. if (id.Length != 0)
  781. AddChildExtensions (conf, module, updateData, path + "/" + id, node.ChildNodes, node.NodeName == "Condition");
  782. }
  783. }
  784. string[] GetDomains ()
  785. {
  786. string[] dirs = fileDatabase.GetDirectories (AddinCachePath);
  787. string[] ids = new string [dirs.Length];
  788. for (int n=0; n<dirs.Length; n++)
  789. ids [n] = Path.GetFileName (dirs [n]);
  790. return ids;
  791. }
  792. public string GetUniqueDomainId ()
  793. {
  794. if (lastDomainId != 0) {
  795. lastDomainId++;
  796. return lastDomainId.ToString ();
  797. }
  798. lastDomainId = 1;
  799. foreach (string s in fileDatabase.GetDirectories (AddinCachePath)) {
  800. string dn = Path.GetFileName (s);
  801. if (dn == GlobalDomain)
  802. continue;
  803. try {
  804. int n = int.Parse (dn);
  805. if (n >= lastDomainId)
  806. lastDomainId = n + 1;
  807. } catch {
  808. }
  809. }
  810. return lastDomainId.ToString ();
  811. }
  812. internal void ResetBasicCachedData ()
  813. {
  814. allSetupInfos = null;
  815. addinSetupInfos = null;
  816. rootSetupInfos = null;
  817. }
  818. internal void ResetCachedData ()
  819. {
  820. ResetBasicCachedData ();
  821. hostIndex = null;
  822. cachedAddinSetupInfos.Clear ();
  823. if (addinEngine != null)
  824. addinEngine.ResetCachedData ();
  825. }
  826. public bool AddinDependsOn (string domain, string id1, string id2)
  827. {
  828. Hashtable visited = new Hashtable ();
  829. return AddinDependsOn (visited, domain, id1, id2);
  830. }
  831. bool AddinDependsOn (Hashtable visited, string domain, string id1, string id2)
  832. {
  833. if (visited.Contains (id1))
  834. return false;
  835. visited.Add (id1, id1);
  836. Addin addin1 = GetInstalledAddin (domain, id1, false);
  837. // We can assumbe that if the add-in is not returned here, it may be a root addin.
  838. if (addin1 == null)
  839. return false;
  840. id2 = Addin.GetIdName (id2);
  841. foreach (Dependency dep in addin1.AddinInfo.Dependencies) {
  842. AddinDependency adep = dep as AddinDependency;
  843. if (adep == null)
  844. continue;
  845. string depid = Addin.GetFullId (addin1.AddinInfo.Namespace, adep.AddinId, null);
  846. if (depid == id2)
  847. return true;
  848. else if (AddinDependsOn (visited, domain, depid, id2))
  849. return true;
  850. }
  851. return false;
  852. }
  853. public void Repair (IProgressStatus monitor, string domain)
  854. {
  855. using (fileDatabase.LockWrite ()) {
  856. try {
  857. if (Directory.Exists (AddinCachePath))
  858. Directory.Delete (AddinCachePath, true);
  859. if (Directory.Exists (AddinFolderCachePath))
  860. Directory.Delete (AddinFolderCachePath, true);
  861. if (File.Exists (HostIndexFile))
  862. File.Delete (HostIndexFile);
  863. }
  864. catch (Exception ex) {
  865. monitor.ReportError ("The add-in registry could not be rebuilt. It may be due to lack of write permissions to the directory: " + AddinDbDir, ex);
  866. }
  867. }
  868. ResetBasicCachedData ();
  869. Update (monitor, domain);
  870. }
  871. public void Update (IProgressStatus monitor, string domain)
  872. {
  873. if (monitor == null)
  874. monitor = new ConsoleProgressStatus (false);
  875. if (RunningSetupProcess)
  876. return;
  877. fatalDatabseError = false;
  878. DateTime tim = DateTime.Now;
  879. RunPendingUninstalls (monitor);
  880. Hashtable installed = new Hashtable ();
  881. bool changesFound = CheckFolders (monitor, domain);
  882. if (monitor.IsCanceled)
  883. return;
  884. if (monitor.LogLevel > 1)
  885. monitor.Log ("Folders checked (" + (int) (DateTime.Now - tim).TotalMilliseconds + " ms)");
  886. if (changesFound) {
  887. // Something has changed, the add-ins need to be re-scanned, but it has
  888. // to be done in an external process
  889. if (domain != null) {
  890. using (fileDatabase.LockRead ()) {
  891. foreach (Addin ainfo in InternalGetInstalledAddins (domain, AddinSearchFlagsInternal.IncludeAddins)) {
  892. installed [ainfo.Id] = ainfo.Id;
  893. }
  894. }
  895. }
  896. RunScannerProcess (monitor);
  897. ResetCachedData ();
  898. registry.NotifyDatabaseUpdated ();
  899. }
  900. if (fatalDatabseError)
  901. monitor.ReportError ("The add-in database could not be updated. It may be due to file corruption. Try running the setup repair utility", null);
  902. // Update the currently loaded add-ins
  903. if (changesFound && domain != null && addinEngine != null && addinEngine.IsInitialized) {
  904. Hashtable newInstalled = new Hashtable ();
  905. foreach (Addin ainfo in GetInstalledAddins (domain, AddinSearchFlagsInternal.IncludeAddins)) {
  906. newInstalled [ainfo.Id] = ainfo.Id;
  907. }
  908. foreach (string aid in installed.Keys) {
  909. // Always try to unload, event if the add-in was not currently loaded.
  910. // Required since the add-ins has to be marked as 'disabled', to avoid
  911. // extensions from this add-in to be loaded
  912. if (!newInstalled.Contains (aid))
  913. addinEngine.UnloadAddin (aid);
  914. }
  915. foreach (string aid in newInstalled.Keys) {
  916. if (!installed.Contains (aid)) {
  917. Addin addin = addinEngine.Registry.GetAddin (aid);
  918. if (addin != null)
  919. addinEngine.ActivateAddin (aid);
  920. }
  921. }
  922. }
  923. }
  924. void RunPendingUninstalls (IProgressStatus monitor)
  925. {
  926. bool changesDone = false;
  927. foreach (var adn in Configuration.GetPendingUninstalls ()) {
  928. HashSet<string> files = new HashSet<string> (adn.Files);
  929. if (AddinManager.CheckAssembliesLoaded (files))
  930. continue;
  931. if (monitor.LogLevel > 1)
  932. monitor.Log ("Uninstalling " + adn.AddinId);
  933. // Make sure all files can be deleted before doing so
  934. bool canUninstall = true;
  935. foreach (string f in adn.Files) {
  936. if (!File.Exists (f))
  937. continue;
  938. try {
  939. File.OpenWrite (f).Close ();
  940. } catch {
  941. canUninstall = false;
  942. break;
  943. }
  944. }
  945. if (!canUninstall)
  946. continue;
  947. foreach (string f in adn.Files) {
  948. try {
  949. if (File.Exists (f))
  950. File.Delete (f);
  951. } catch {
  952. canUninstall = false;
  953. }
  954. }
  955. if (canUninstall) {
  956. Configuration.UnregisterForUninstall (adn.AddinId);
  957. changesDone = true;
  958. }
  959. }
  960. if (changesDone)
  961. SaveConfiguration ();
  962. }
  963. void RunScannerProcess (IProgressStatus monitor)
  964. {
  965. ISetupHandler setup = GetSetupHandler ();
  966. IProgressStatus scanMonitor = monitor;
  967. ArrayList pparams = new ArrayList ();
  968. bool retry = false;
  969. do {
  970. try {
  971. if (monitor.LogLevel > 1)
  972. monitor.Log ("Looking for addins");
  973. setup.Scan (scanMonitor, registry, null, (string[]) pparams.ToArray (typeof(string)));
  974. retry = false;
  975. }
  976. catch (Exception ex) {
  977. ProcessFailedException pex = ex as ProcessFailedException;
  978. if (pex != null) {
  979. // Get the last logged operation.
  980. if (pex.LastLog.StartsWith ("scan:")) {
  981. // It crashed while scanning a file. Add the file to the ignore list and try again.
  982. string file = pex.LastLog.Substring (5);
  983. pparams.Add (file);
  984. monitor.ReportWarning ("Could not scan file: " + file);
  985. retry = true;
  986. continue;
  987. }
  988. }
  989. fatalDatabseError = true;
  990. // If the process has crashed, try to do a new scan, this time using verbose log,
  991. // to give the user more information about the origin of the crash.
  992. if (pex != null && !retry) {
  993. monitor.ReportError ("Add-in scan operation failed. The Mono runtime may have encountered an error while trying to load an assembly.", null);
  994. if (monitor.LogLevel <= 1) {
  995. // Re-scan again using verbose log, to make it easy to find the origin of the error.
  996. retry = true;
  997. scanMonitor = new ConsoleProgressStatus (true);
  998. }
  999. } else
  1000. retry = false;
  1001. if (!retry) {
  1002. monitor.ReportError ("Add-in scan operation failed", (ex is ProcessFailedException ? null : ex));
  1003. monitor.Cancel ();
  1004. return;
  1005. }
  1006. }
  1007. }
  1008. while (retry);
  1009. }
  1010. bool DatabaseInfrastructureCheck (IProgressStatus monitor)
  1011. {
  1012. // Do some sanity check, to make sure the basic database infrastructure can be created
  1013. bool hasChanges = false;
  1014. try {
  1015. if (!Directory.Exists (AddinCachePath)) {
  1016. Directory.CreateDirectory (AddinCachePath);
  1017. hasChanges = true;
  1018. }
  1019. if (!Directory.Exists (AddinFolderCachePath)) {
  1020. Directory.CreateDirectory (AddinFolderCachePath);
  1021. hasChanges = true;
  1022. }
  1023. // Make sure we can write in those folders
  1024. Util.CheckWrittableFloder (AddinCachePath);
  1025. Util.CheckWrittableFloder (AddinFolderCachePath);
  1026. fatalDatabseError = false;
  1027. }
  1028. catch (Exception ex) {
  1029. monitor.ReportError ("Add-in cache directory could not be created", ex);
  1030. fatalDatabseError = true;
  1031. monitor.Cancel ();
  1032. }
  1033. return hasChanges;
  1034. }
  1035. internal bool CheckFolders (IProgressStatus monitor, string domain)
  1036. {
  1037. using (fileDatabase.LockRead ()) {
  1038. AddinScanResult scanResult = new AddinScanResult ();
  1039. scanResult.CheckOnly = true;
  1040. scanResult.Domain = domain;
  1041. InternalScanFolders (monitor, scanResult);
  1042. return scanResult.ChangesFound;
  1043. }
  1044. }
  1045. internal void ScanFolders (IProgressStatus monitor, string currentDomain, string folderToScan, StringCollection filesToIgnore)
  1046. {
  1047. AddinScanResult res = new AddinScanResult ();
  1048. res.Domain = currentDomain;
  1049. res.AddPathsToIgnore (filesToIgnore);
  1050. ScanFolders (monitor, res);
  1051. }
  1052. void ScanFolders (IProgressStatus monitor, AddinScanResult scanResult)
  1053. {
  1054. IDisposable checkLock = null;
  1055. if (scanResult.CheckOnly)
  1056. checkLock = fileDatabase.LockRead ();
  1057. else {
  1058. // All changes are done in a transaction, which won't be committed until
  1059. // all files have been updated.
  1060. if (!fileDatabase.BeginTransaction ()) {
  1061. // The database is already being updated. Can't do anything for now.
  1062. return;
  1063. }
  1064. }
  1065. EventInfo einfo = typeof(AppDomain).GetEvent ("ReflectionOnlyAssemblyResolve");
  1066. ResolveEventHandler resolver = new ResolveEventHandler (OnResolveAddinAssembly);
  1067. try
  1068. {
  1069. // Perform the add-in scan
  1070. if (!scanResult.CheckOnly) {
  1071. AppDomain.CurrentDomain.AssemblyResolve += resolver;
  1072. if (einfo != null) einfo.AddEventHandler (AppDomain.CurrentDomain, resolver);
  1073. }
  1074. InternalScanFolders (monitor, scanResult);
  1075. if (!scanResult.CheckOnly)
  1076. fileDatabase.CommitTransaction ();
  1077. }
  1078. catch {
  1079. if (!scanResult.CheckOnly)
  1080. fileDatabase.RollbackTransaction ();
  1081. throw;
  1082. }
  1083. finally {
  1084. currentScanResult = null;
  1085. if (scanResult.CheckOnly)
  1086. checkLock.Dispose ();
  1087. else {
  1088. AppDomain.CurrentDomain.AssemblyResolve -= resolver;
  1089. if (einfo != null) einfo.RemoveEventHandler (AppDomain.CurrentDomain, resolver);
  1090. }
  1091. }
  1092. }
  1093. void InternalScanFolders (IProgressStatus monitor, AddinScanResult scanResult)
  1094. {
  1095. try {
  1096. fs.ScanStarted ();
  1097. InternalScanFolders2 (monitor, scanResult);
  1098. } finally {
  1099. fs.ScanFinished ();
  1100. }
  1101. }
  1102. void InternalScanFolders2 (IProgressStatus monitor, AddinScanResult scanResult)
  1103. {
  1104. DateTime tim = DateTime.Now;
  1105. DatabaseInfrastructureCheck (monitor);
  1106. if (monitor.IsCanceled)
  1107. return;
  1108. try {
  1109. scanResult.HostIndex = GetAddinHostIndex ();
  1110. }
  1111. catch (Exception ex) {
  1112. if (scanResult.CheckOnly) {
  1113. scanResult.ChangesFound = true;
  1114. return;
  1115. }
  1116. monitor.ReportError ("Add-in root index is corrupt. The add-in database will be regenerated.", ex);
  1117. scanResult.RegenerateAllData = true;
  1118. }
  1119. AddinScanner scanner = new AddinScanner (this, scanResult, monitor);
  1120. // Check if any of the previously scanned folders has been deleted
  1121. foreach (string file in Directory.GetFiles (AddinFolderCachePath, "*.data")) {
  1122. AddinScanFolderInfo folderInfo;
  1123. bool res = ReadFolderInfo (monitor, file, out folderInfo);
  1124. bool validForDomain = scanResult.Domain == null || folderInfo.Domain == GlobalDomain || folderInfo.Domain == scanResult.Domain;
  1125. if (!res || (validForDomain && !fs.DirectoryExists (folderInfo.Folder))) {
  1126. if (res) {
  1127. // Folder has been deleted. Remove the add-ins it had.
  1128. scanner.UpdateDeletedAddins (monitor, folderInfo, scanResult);
  1129. }
  1130. else {
  1131. // Folder info file corrupt. Regenerate all.
  1132. scanResult.ChangesFound = true;
  1133. scanResult.RegenerateRelationData = true;
  1134. }
  1135. if (!scanResult.CheckOnly)
  1136. SafeDelete (monitor, file);
  1137. else if (scanResult.ChangesFound)
  1138. return;
  1139. }
  1140. }
  1141. // Look for changes in the add-in folders
  1142. if (registry.StartupDirectory != null)
  1143. scanner.ScanFolder (monitor, registry.StartupDirectory, null, scanResult);
  1144. if (scanResult.CheckOnly && (scanResult.ChangesFound || monitor.IsCanceled))
  1145. return;
  1146. if (scanResult.Domain == null)
  1147. scanner.ScanFolder (monitor, HostsPath, GlobalDomain, scanResult);
  1148. if (scanResult.CheckOnly && (scanResult.ChangesFound || monitor.IsCanceled))
  1149. return;
  1150. foreach (string dir in registry.GlobalAddinDirectories) {
  1151. if (scanResult.CheckOnly && (scanResult.ChangesFound || monitor.IsCanceled))
  1152. return;
  1153. scanner.ScanFolderRec (monitor, dir, GlobalDomain, scanResult);
  1154. }
  1155. if (scanResult.CheckOnly || !scanResult.ChangesFound)
  1156. return;
  1157. // Scan the files which have been modified
  1158. currentScanResult = scanResult;
  1159. foreach (FileToScan file in scanResult.FilesToScan)
  1160. scanner.ScanFile (monitor, file.File, file.AddinScanFolderInfo, scanResult);
  1161. // Save folder info
  1162. foreach (AddinScanFolderInfo finfo in scanResult.ModifiedFolderInfos)
  1163. SaveFolderInfo (monitor, finfo);
  1164. if (monitor.LogLevel > 1)
  1165. monitor.Log ("Folders scan completed (" + (int) (DateTime.Now - tim).TotalMilliseconds + " ms)");
  1166. SaveAddinHostIndex ();
  1167. ResetCachedData ();
  1168. if (!scanResult.ChangesFound) {
  1169. if (monitor.LogLevel > 1)
  1170. monitor.Log ("No changes found");
  1171. return;
  1172. }
  1173. tim = DateTime.Now;
  1174. try {
  1175. if (scanResult.RegenerateRelationData) {
  1176. if (monitor.LogLevel > 1)
  1177. monitor.Log ("Regenerating all add-in relations.");
  1178. scanResult.AddinsToUpdate = null;
  1179. scanResult.AddinsToUpdateRelations = null;
  1180. }
  1181. GenerateAddinExtensionMapsInternal (monitor, scanResult.AddinsToUpdate, scanResult.AddinsToUpdateRelations, scanResult.RemovedAddins);
  1182. }
  1183. catch (Exception ex) {
  1184. fatalDatabseError = true;
  1185. monitor.ReportError ("The add-in database could not be updated. It may be due to file corruption. Try running the setup repair utility", ex);
  1186. }
  1187. if (monitor.LogLevel > 1)
  1188. monitor.Log ("Add-in relations analyzed (" + (int) (DateTime.Now - tim).TotalMilliseconds + " ms)");
  1189. SaveAddinHostIndex ();
  1190. }
  1191. public void ParseAddin (IProgressStatus progressStatus, string domain, string file, string outFile, bool inProcess)
  1192. {
  1193. if (!inProcess) {
  1194. ISetupHandler setup = GetSetupHandler ();
  1195. setup.GetAddinDescription (progressStatus, registry, Path.GetFullPath (file), outFile);
  1196. return;
  1197. }
  1198. using (fileDatabase.LockRead ())
  1199. {
  1200. // First of all, check if the file belongs to a registered add-in
  1201. AddinScanFolderInfo finfo;
  1202. if (GetFolderInfoForPath (progressStatus, Path.GetDirectoryName (file), out finfo) && finfo != null) {
  1203. AddinFileInfo afi = finfo.GetAddinFileInfo (file);
  1204. if (afi != null && afi.IsAddin) {
  1205. AddinDescription adesc;
  1206. GetAddinDescription (progressStatus, afi.Domain, afi.AddinId, file, out adesc);
  1207. if (adesc != null)
  1208. adesc.Save (outFile);
  1209. return;
  1210. }
  1211. }
  1212. AddinScanResult sr = new AddinScanResult ();
  1213. sr.Domain = domain;
  1214. AddinScanner scanner = new AddinScanner (this, sr, progressStatus);
  1215. SingleFileAssemblyResolver res = new SingleFileAssemblyResolver (progressStatus, registry, scanner);
  1216. ResolveEventHandler resolver = new ResolveEventHandler (res.Resolve);
  1217. EventInfo einfo = typeof(AppDomain).GetEvent ("ReflectionOnlyAssemblyResolve");
  1218. try {
  1219. AppDomain.CurrentDomain.AssemblyResolve += resolver;
  1220. if (einfo != null) einfo.AddEventHandler (AppDomain.CurrentDomain, resolver);
  1221. AddinDescription desc = scanner.ScanSingleFile (progressStatus, file, sr);
  1222. if (desc != null)
  1223. desc.Save (outFile);
  1224. }
  1225. finally {
  1226. AppDomain.CurrentDomain.AssemblyResolve -= resolver;
  1227. if (einfo != null) einfo.RemoveEventHandler (AppDomain.CurrentDomain, resolver);
  1228. }
  1229. }
  1230. }
  1231. public string GetFolderDomain (IProgressStatus progressStatus, string path)
  1232. {
  1233. AddinScanFolderInfo folderInfo;
  1234. if (GetFolderInfoForPath (progressStatus, path, out folderInfo) && folderInfo != null && !folderInfo.SharedFolder)
  1235. return folderInfo.Domain;
  1236. else
  1237. return UnknownDomain;
  1238. }
  1239. Assembly OnResolveAddinAssembly (object s, ResolveEventArgs args)
  1240. {
  1241. string file = currentScanResult != null ? currentScanResult.GetAssemblyLocation (args.Name) : null;
  1242. if (file != null)
  1243. return Util.LoadAssemblyForReflection (file);
  1244. else {
  1245. if (!args.Name.StartsWith ("Mono.Addins.CecilReflector"))
  1246. Console.WriteLine ("Assembly not found: " + args.Name);
  1247. return null;
  1248. }
  1249. }
  1250. public string GetFolderConfigFile (string path)
  1251. {
  1252. path = Path.GetFullPath (path);
  1253. string s = path.Replace ("_", "__");
  1254. s = s.Replace (Path.DirectorySeparatorChar, '_');
  1255. s = s.Replace (Path.AltDirectorySeparatorChar, '_');
  1256. s = s.Replace (Path.VolumeSeparatorChar, '_');
  1257. return Path.Combine (AddinFolderCachePath, s + ".data");
  1258. }
  1259. internal void UninstallAddin (IProgressStatus monitor, string domain, string addinId, string addinFile, AddinScanResult scanResult)
  1260. {
  1261. AddinDescription desc;
  1262. if (!GetAddinDescription (monitor, domain, addinId, addinFile, out desc)) {
  1263. // If we can't get information about the old assembly, just regenerate all relation data
  1264. scanResult.RegenerateRelationData = true;
  1265. return;
  1266. }
  1267. scanResult.AddRemovedAddin (addinId);
  1268. // If the add-in didn't exist, there is nothing left to do
  1269. if (desc == null)
  1270. return;
  1271. // If the add-in already existed, the dependencies of the old add-in need to be re-analized
  1272. Util.AddDependencies (desc, scanResult);
  1273. if (desc.IsRoot)
  1274. scanResult.HostIndex.RemoveHostData (desc.AddinId, desc.AddinFile);
  1275. RemoveAddinDescriptionFile (monitor, desc.FileName);
  1276. }
  1277. public bool GetAddinDescription (IProgressStatus monitor, string domain, string addinId, string addinFile, out AddinDescription description)
  1278. {
  1279. // If the same add-in is installed in different folders (in the same domain) there will be several .maddin files for it,
  1280. // 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, ...)
  1281. // We need to return the .maddin whose AddinFile matches the one being requested
  1282. addinFile = Path.GetFullPath (addinFile);
  1283. int altNum = 1;
  1284. string baseFile = GetDescriptionPath (domain, addinId);
  1285. string file = baseFile;
  1286. bool failed = false;
  1287. do {
  1288. if (!ReadAddinDescription (monitor, file, out description)) {
  1289. // Remove the AddinDescription here since it is corrupted.
  1290. // Avoids creating alternate versions of corrupted files when later calling SaveDescription.
  1291. RemoveAddinDescriptionFile (monitor, file);
  1292. failed = true;
  1293. continue;
  1294. }
  1295. if (description == null)
  1296. break;
  1297. if (Path.GetFullPath (description.AddinFile) == addinFile)
  1298. return true;
  1299. file = baseFile + "_" + (++altNum);
  1300. }
  1301. while (fileDatabase.Exists (file));
  1302. // File not found. Return false only if there has been any read error.
  1303. description = null;
  1304. return failed;
  1305. }
  1306. bool RemoveAddinDescriptionFile (IProgressStatus monitor, string file)
  1307. {
  1308. // Removes an add-in description and shifts up alternate instances of the description file
  1309. // (so xxx,1.0.maddin_2 will become xxx,1.0.maddin, xxx,1.0.maddin_3 -> xxx,1.0.maddin_2, etc)
  1310. if (!SafeDelete (monitor, file))
  1311. return false;
  1312. int dversion;
  1313. if (file.EndsWith (".maddin"))
  1314. dversion = 2;
  1315. else {
  1316. int i = file.LastIndexOf ('_');
  1317. dversion = 1 + int.Parse (file.Substring (i + 1));
  1318. file = file.Substring (0, i);
  1319. }
  1320. while (fileDatabase.Exists (file + "_" + dversion)) {
  1321. string newFile = dversion == 2 ? file : file + "_" + (dversion-1);
  1322. try {
  1323. fileDatabase.Rename (file + "_" + dversion, newFile);
  1324. } catch (Exception ex) {
  1325. if (monitor.LogLevel > 1) {
  1326. monitor.Log ("Could not rename file '" + file + "_" + dversion + "' to '" + newFile + "'");
  1327. monitor.Log (ex.ToString ());
  1328. }
  1329. }
  1330. dversion++;
  1331. }
  1332. string dir = Path.GetDirectoryName (file);
  1333. if (fileDatabase.DirectoryIsEmpty (dir))
  1334. SafeDeleteDir (monitor, dir);
  1335. if (dversion == 2) {
  1336. // All versions of the add-in removed.
  1337. SafeDeleteDir (monitor, Path.Combine (AddinPrivateDataPath, Path.GetFileNameWithoutExtension (file)));
  1338. }
  1339. return true;
  1340. }
  1341. public bool ReadAddinDescription (IProgressStatus monitor, string file, out AddinDescription description)
  1342. {
  1343. try {
  1344. description = AddinDescription.ReadBinary (fileDatabase, file);
  1345. if (description != null)
  1346. description.OwnerDatabase = this;
  1347. return true;
  1348. }
  1349. catch (Exception ex) {
  1350. if (monitor == null)
  1351. throw;
  1352. description = null;
  1353. monitor.Rep

Large files files are truncated, but you can click here to view the full file