Logo Search packages:      
Sourcecode: f-spot version File versions

AddinDatabase.cs

//
// AddinDatabase.cs
//
// Author:
//   Lluis Sanchez Gual
//
// Copyright (C) 2007 Novell, Inc (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//


using System;
using System.Threading;
using System.Collections;
using System.Collections.Specialized;
using System.IO;
using System.Xml;
using System.Reflection;
using Mono.Addins.Description;

namespace Mono.Addins.Database
{
      class AddinDatabase
      {
            public const string GlobalDomain = "global";
            
            public const string VersionTag = "001";

            ArrayList allSetupInfos;
            ArrayList addinSetupInfos;
            ArrayList rootSetupInfos;
            internal static bool RunningSetupProcess;
            bool fatalDatabseError;
            Hashtable cachedAddinSetupInfos = new Hashtable ();
            AddinScanResult currentScanResult;
            AddinHostIndex hostIndex;
            FileDatabase fileDatabase;
            string addinDbDir;
            DatabaseConfiguration config = null;
            AddinRegistry registry;
            int lastDomainId;
            
            public AddinDatabase (AddinRegistry registry)
            {
                  this.registry = registry;
                  addinDbDir = Path.Combine (registry.RegistryPath, "addin-db-" + VersionTag);
                  fileDatabase = new FileDatabase (AddinDbDir);
            }
            
            string AddinDbDir {
                  get { return addinDbDir; }
            }
            
            public string AddinCachePath {
                  get { return Path.Combine (AddinDbDir, "addin-data"); }
            }
            
            public string AddinFolderCachePath {
                  get { return Path.Combine (AddinDbDir, "addin-dir-data"); }
            }
            
            public string AddinPrivateDataPath {
                  get { return Path.Combine (AddinDbDir, "addin-priv-data"); }
            }
            
            public string HostsPath {
                  get { return Path.Combine (AddinDbDir, "hosts"); }
            }
            
            string HostIndexFile {
                  get { return Path.Combine (AddinDbDir, "host-index"); }
            }
            
            string ConfigFile {
                  get { return Path.Combine (AddinDbDir, "config.xml"); }
            }
            
            internal bool IsGlobalRegistry {
                  get {
                        return registry.RegistryPath == AddinRegistry.GlobalRegistryPath;
                  }
            }
            
            public void Clear ()
            {
                  if (Directory.Exists (AddinCachePath))
                        Directory.Delete (AddinCachePath, true);
                  if (Directory.Exists (AddinFolderCachePath))
                        Directory.Delete (AddinFolderCachePath, true);
            }
            
            public ExtensionNodeSet FindNodeSet (string domain, string addinId, string id)
            {
                  return FindNodeSet (domain, addinId, id, new Hashtable ());
            }
            
            ExtensionNodeSet FindNodeSet (string domain, string addinId, string id, Hashtable visited)
            {
                  if (visited.Contains (addinId))
                        return null;
                  visited.Add (addinId, addinId);
                  Addin addin = GetInstalledAddin (domain, addinId, true, false);
                  if (addin == null)
                        return null;
                  AddinDescription desc = addin.Description;
                  if (desc == null)
                        return null;
                  foreach (ExtensionNodeSet nset in desc.ExtensionNodeSets)
                        if (nset.Id == id)
                              return nset;
                  
                  // Not found in the add-in. Look on add-ins on which it depends
                  
                  foreach (Dependency dep in desc.MainModule.Dependencies) {
                        AddinDependency adep = dep as AddinDependency;
                        if (adep == null) continue;
                        
                        string aid = Addin.GetFullId (desc.Namespace, adep.AddinId, adep.Version);
                        ExtensionNodeSet nset = FindNodeSet (domain, aid, id, visited);
                        if (nset != null)
                              return nset;
                  }
                  return null;
            }
            
            public ArrayList GetInstalledAddins (string domain, AddinType type)
            {
                  if (type == AddinType.All) {
                        if (allSetupInfos != null)
                              return allSetupInfos;
                  }
                  else if (type == AddinType.Addin) {
                        if (addinSetupInfos != null)
                              return addinSetupInfos;
                  }
                  else {
                        if (rootSetupInfos != null)
                              return rootSetupInfos;
                  }
            
                  InternalCheck (domain);
                  
                  using (fileDatabase.LockRead ()) {
                        return InternalGetInstalledAddins (domain, null, type);
                  }
            }
            
            ArrayList InternalGetInstalledAddins (string domain, AddinType type)
            {
                  return InternalGetInstalledAddins (domain, null, type);
            }
            
            ArrayList InternalGetInstalledAddins (string domain, string idFilter, AddinType type)
            {
                  if (allSetupInfos == null) {
                        ArrayList alist = new ArrayList ();

                        // Global add-ins are valid for any private domain
                        if (domain != AddinDatabase.GlobalDomain)
                              FindInstalledAddins (alist, AddinDatabase.GlobalDomain, idFilter);

                        FindInstalledAddins (alist, domain, idFilter);
                        if (idFilter != null)
                              return alist;
                        allSetupInfos = alist;
                  }
                  if (type == AddinType.All)
                        return FilterById (allSetupInfos, idFilter);
                  
                  if (type == AddinType.Addin) {
                        if (addinSetupInfos == null) {
                              addinSetupInfos = new ArrayList ();
                              foreach (Addin adn in allSetupInfos)
                                    if (!adn.Description.IsRoot)
                                          addinSetupInfos.Add (adn);
                        }
                        return FilterById (addinSetupInfos, idFilter);
                  }
                  else {
                        if (rootSetupInfos == null) {
                              rootSetupInfos = new ArrayList ();
                              foreach (Addin adn in allSetupInfos)
                                    if (adn.Description.IsRoot)
                                          rootSetupInfos.Add (adn);
                        }
                        return FilterById (rootSetupInfos, idFilter);
                  }
            }
            
            ArrayList FilterById (ArrayList addins, string id)
            {
                  if (id == null)
                        return addins;
                  ArrayList list = new ArrayList ();
                  foreach (Addin adn in addins) {
                        if (Addin.GetIdName (adn.Id) == id)
                              list.Add (adn);
                  }
                  return list;
            }

            void FindInstalledAddins (ArrayList result, string domain, string idFilter)
            {
                  if (idFilter == null) 
                        idFilter = "*";
                  string dir = Path.Combine (AddinCachePath, domain);
                  if (Directory.Exists (dir)) {
                        foreach (string file in fileDatabase.GetDirectoryFiles (dir, idFilter + ",*.maddin")) {
                              string id = Path.GetFileNameWithoutExtension (file);
                              result.Add (GetInstalledDomainAddin (domain, id, true, false, false));
                        }
                  }
            }

            public Addin GetInstalledAddin (string domain, string id)
            {
                  return GetInstalledAddin (domain, id, false, false);
            }
            
            public Addin GetInstalledAddin (string domain, string id, bool exactVersionMatch)
            {
                  return GetInstalledAddin (domain, id, exactVersionMatch, false);
            }
            
            public Addin GetInstalledAddin (string domain, string id, bool exactVersionMatch, bool enabledOnly)
            {
                  // Try the given domain, and if not found, try the shared domain
                  Addin ad = GetInstalledDomainAddin (domain, id, exactVersionMatch, enabledOnly, true);
                  if (ad != null)
                        return ad;
                  if (domain != AddinDatabase.GlobalDomain)
                        return GetInstalledDomainAddin (AddinDatabase.GlobalDomain, id, exactVersionMatch, enabledOnly, true);
                  else
                        return null;
            }
            
            Addin GetInstalledDomainAddin (string domain, string id, bool exactVersionMatch, bool enabledOnly, bool dbLockCheck)
            {
                  Addin sinfo = null;
                  string idd = id + " " + domain;
                  object ob = cachedAddinSetupInfos [idd];
                  if (ob != null) {
                        sinfo = ob as Addin;
                        if (sinfo != null) {
                              if (!enabledOnly || sinfo.Enabled)
                                    return sinfo;
                              if (exactVersionMatch)
                                    return null;
                        }
                        else if (enabledOnly)
                              // Ignore the 'not installed' flag when disabled add-ins are allowed
                              return null;
                  }
            
                  if (dbLockCheck)
                        InternalCheck (domain);
                  
                  using ((dbLockCheck ? fileDatabase.LockRead () : null))
                  {
                        string path = GetDescriptionPath (domain, id);
                        if (sinfo == null && fileDatabase.Exists (path)) {
                              sinfo = new Addin (this, path);
                              cachedAddinSetupInfos [idd] = sinfo;
                              if (!enabledOnly || sinfo.Enabled)
                                    return sinfo;
                              if (exactVersionMatch) {
                                    // Cache lookups with negative result
                                    cachedAddinSetupInfos [idd] = this;
                                    return null;
                              }
                        }
                        
                        // Exact version not found. Look for a compatible version
                        if (!exactVersionMatch) {
                              sinfo = null;
                              string version, name, bestVersion = null;
                              Addin.GetIdParts (id, out name, out version);
                              
                              foreach (Addin ia in InternalGetInstalledAddins (domain, name, AddinType.All)) 
                              {
                                    if ((!enabledOnly || ia.Enabled) &&
                                        (version.Length == 0 || ia.SupportsVersion (version)) && 
                                        (bestVersion == null || Addin.CompareVersions (bestVersion, ia.Version) > 0)) 
                                    {
                                          bestVersion = ia.Version;
                                          sinfo = ia;
                                    }
                              }
                              if (sinfo != null) {
                                    cachedAddinSetupInfos [idd] = sinfo;
                                    return sinfo;
                              }
                        }
                        
                        // Cache lookups with negative result
                        // Ignore the 'not installed' flag when disabled add-ins are allowed
                        if (enabledOnly)
                              cachedAddinSetupInfos [idd] = this;
                        return null;
                  }
            }
            
            public void Shutdown ()
            {
                  ResetCachedData ();
            }
            
            public Addin GetAddinForHostAssembly (string domain, string assemblyLocation)
            {
                  InternalCheck (domain);
                  Addin ainfo = null;
                  
                  object ob = cachedAddinSetupInfos [assemblyLocation];
                  if (ob != null)
                        return ob as Addin; // Don't use a cast here is ob may not be an Addin.

                  AddinHostIndex index = GetAddinHostIndex ();
                  string addin, addinFile, rdomain;
                  if (index.GetAddinForAssembly (assemblyLocation, out addin, out addinFile, out rdomain)) {
                        string sid = addin + " " + rdomain;
                        ainfo = cachedAddinSetupInfos [sid] as Addin;
                        if (ainfo == null)
                              ainfo = new Addin (this, GetDescriptionPath (rdomain, addin));
                        cachedAddinSetupInfos [assemblyLocation] = ainfo;
                        cachedAddinSetupInfos [addin + " " + rdomain] = ainfo;
                  }
                  
                  return ainfo;
            }
            
            
            public bool IsAddinEnabled (string domain, string id)
            {
                  Addin ainfo = GetInstalledAddin (domain, id);
                  if (ainfo != null)
                        return ainfo.Enabled;
                  else
                        return false;
            }
            
            internal bool IsAddinEnabled (string domain, string id, bool exactVersionMatch)
            {
                  if (!exactVersionMatch)
                        return IsAddinEnabled (domain, id);
                  Addin ainfo = GetInstalledAddin (domain, id, exactVersionMatch, false);
                  if (ainfo == null)
                        return false;
                  return Configuration.IsEnabled (id, ainfo.AddinInfo.EnabledByDefault);
            }
            
            public void EnableAddin (string domain, string id)
            {
                  EnableAddin (domain, id, true);
            }
            
            internal void EnableAddin (string domain, string id, bool exactVersionMatch)
            {
                  Addin ainfo = GetInstalledAddin (domain, id, exactVersionMatch, false);
                  if (ainfo == null)
                        // It may be an add-in root
                        return;

                  if (IsAddinEnabled (domain, id))
                        return;
                  
                  // Enable required add-ins
                  
                  foreach (Dependency dep in ainfo.AddinInfo.Dependencies) {
                        if (dep is AddinDependency) {
                              AddinDependency adep = dep as AddinDependency;
                              string adepid = Addin.GetFullId (ainfo.AddinInfo.Namespace, adep.AddinId, adep.Version);
                              EnableAddin (domain, adepid, false);
                        }
                  }

                  Configuration.SetStatus (id, true, ainfo.AddinInfo.EnabledByDefault);
                  SaveConfiguration ();

                  if (AddinManager.IsInitialized && AddinManager.Registry.RegistryPath == registry.RegistryPath)
                        AddinManager.SessionService.ActivateAddin (id);
            }
            
            public void DisableAddin (string domain, string id)
            {
                  Addin ai = GetInstalledAddin (domain, id, true);
                  if (ai == null)
                        throw new InvalidOperationException ("Extension '" + id + "' not installed.");

                  if (!IsAddinEnabled (domain, id))
                        return;
                  
                  Configuration.SetStatus (id, false, ai.AddinInfo.EnabledByDefault);
                  SaveConfiguration ();
                  
                  // Disable all add-ins which depend on it
                  
                  try {
                        string idName = Addin.GetIdName (id);
                        
                        foreach (Addin ainfo in GetInstalledAddins (domain, AddinType.Addin)) {
                              foreach (Dependency dep in ainfo.AddinInfo.Dependencies) {
                                    AddinDependency adep = dep as AddinDependency;
                                    if (adep == null)
                                          continue;
                                    
                                    string adepid = Addin.GetFullId (ainfo.AddinInfo.Namespace, adep.AddinId, null);
                                    if (adepid != idName)
                                          continue;
                                    
                                    // The add-in that has been disabled, might be a requeriment of this one, or maybe not
                                    // if there is an older version available. Check it now.
                                    
                                    adepid = Addin.GetFullId (ainfo.AddinInfo.Namespace, adep.AddinId, adep.Version);
                                    Addin adepinfo = GetInstalledAddin (domain, adepid, false, true);
                                    
                                    if (adepinfo == null) {
                                          DisableAddin (domain, ainfo.Id);
                                          break;
                                    }
                              }
                        }
                  }
                  catch {
                        // If something goes wrong, enable the add-in again
                        Configuration.SetStatus (id, true, ai.AddinInfo.EnabledByDefault);
                        SaveConfiguration ();
                        throw;
                  }

                  if (AddinManager.IsInitialized && AddinManager.Registry.RegistryPath == registry.RegistryPath)
                        AddinManager.SessionService.UnloadAddin (id);
            }           

            internal string GetDescriptionPath (string domain, string id)
            {
                  return Path.Combine (Path.Combine (AddinCachePath, domain), id + ".maddin");
            }
            
            void InternalCheck (string domain)
            {
                  // If the database is broken, don't try to regenerate it at every check.
                  if (fatalDatabseError)
                        return;

                  bool update = false;
                  using (fileDatabase.LockRead ()) {
                        if (!Directory.Exists (AddinCachePath)) {
                              update = true;
                        }
                  }
                  if (update)
                        Update (null, domain);
            }
            
            void GenerateAddinExtensionMapsInternal (IProgressStatus monitor, ArrayList addinsToUpdate, ArrayList removedAddins)
            {
                  AddinUpdateData updateData = new AddinUpdateData (this, monitor);
                  
                  // Clear cached data
                  cachedAddinSetupInfos.Clear ();
                  
                  // Collect all information
                  
                  AddinIndex addinHash = new AddinIndex ();
                  
                  if (monitor.LogLevel > 1)
                        monitor.Log ("Generating add-in extension maps");
                  
                  Hashtable changedAddins = null;
                  ArrayList descriptionsToSave = new ArrayList ();
                  ArrayList files = new ArrayList ();
                  
                  bool partialGeneration = addinsToUpdate != null;
                  string[] domains = GetDomains ();
                  
                  // Get the files to be updated
                  
                  if (partialGeneration) {
                        changedAddins = new Hashtable ();
                        foreach (string sa in addinsToUpdate) {
                              changedAddins [sa] = sa;
                              
                              // Look for all versions of the add-in, because this id may be the id of a reference,
                              // and the exact reference version may not be installed.
                              string s = sa;
                              int i = s.LastIndexOf (',');
                              if (i != -1)
                                    s = s.Substring (0, i);
                              s += ",*";
                              
                              // Look for the add-in in any of the existing folders
                              foreach (string domain in domains) {
                                    string mp = GetDescriptionPath (domain, s);
                                    string dir = Path.GetDirectoryName (mp);
                                    string pat = Path.GetFileName (mp);
                                    foreach (string fmp in fileDatabase.GetDirectoryFiles (dir, pat)) {
                                          if (files.Contains (fmp))
                                                continue;
                                          files.Add (fmp);
                                          string an = Path.GetFileNameWithoutExtension (fmp);
                                          changedAddins [an] = an;
                                    }
                              }
                        }
                        foreach (string s in removedAddins)
                              changedAddins [s] = s;
                  }
                  else {
                        foreach (string dom in domains)
                              files.AddRange (fileDatabase.GetDirectoryFiles (Path.Combine (AddinCachePath, dom), "*.maddin"));
                  }
                  
                  // Load the descriptions.
                  foreach (string file in files) {
                  
                        AddinDescription conf;
                        if (!ReadAddinDescription (monitor, file, out conf)) {
                              SafeDelete (monitor, file);
                              continue;
                        }

                        // If the original file does not exist, the description can be deleted
                        if (!File.Exists (conf.AddinFile)) {
                              SafeDelete (monitor, file);
                              continue;
                        }
                        
                        // Remove old data from the description. If changedAddins==null, removes all data.
                        // Otherwise, removes data only from the addins in the table.
                        
                        conf.UnmergeExternalData (changedAddins);
                        descriptionsToSave.Add (conf);
                        
                        addinHash.Add (conf);
                  }

                  // Sort the add-ins, to make sure add-ins are processed before
                  // all their dependencies
                  ArrayList sorted = addinHash.GetSortedAddins ();

                  // Register extension points and node sets
                  foreach (AddinDescription conf in sorted)
                        CollectExtensionPointData (conf, updateData);
                  
                  // Register extensions
                  foreach (AddinDescription conf in sorted)
                        CollectExtensionData (conf, updateData);
                  
                  // Save the maps
                  foreach (AddinDescription conf in descriptionsToSave)
                        conf.SaveBinary (fileDatabase);
                  
                  if (monitor.LogLevel > 1) {
                        monitor.Log ("Addin relation map generated.");
                        monitor.Log ("  Addins Updated: " + descriptionsToSave.Count);
                        monitor.Log ("  Extension points: " + updateData.RelExtensionPoints);
                        monitor.Log ("  Extensions: " + updateData.RelExtensions);
                        monitor.Log ("  Extension nodes: " + updateData.RelExtensionNodes);
                        monitor.Log ("  Node sets: " + updateData.RelNodeSetTypes);
                  }
            }
            
            // Collects extension data in a hash table. The key is the path, the value is a list
            // of add-ins ids that extend that path
            
            void CollectExtensionPointData (AddinDescription conf, AddinUpdateData updateData)
            {
                  foreach (ExtensionNodeSet nset in conf.ExtensionNodeSets) {
                        try {
                              updateData.RegisterNodeSet (conf, nset);
                              updateData.RelNodeSetTypes++;
                        } catch (Exception ex) {
                              throw new InvalidOperationException ("Error reading node set: " + nset.Id, ex);
                        }
                  }
                  
                  foreach (ExtensionPoint ep in conf.ExtensionPoints) {
                        try {
                              updateData.RegisterExtensionPoint (conf, ep);
                              updateData.RelExtensionPoints++;
                        } catch (Exception ex) {
                              throw new InvalidOperationException ("Error reading extension point: " + ep.Path, ex);
                        }
                  }
            }
            
            void CollectExtensionData (AddinDescription conf, AddinUpdateData updateData)
            {
                  foreach (ModuleDescription module in conf.AllModules) {
                        foreach (Extension ext in module.Extensions) {
                              updateData.RelExtensions++;
                              updateData.RegisterExtension (conf, module, ext);
                              AddChildExtensions (conf, module, updateData, ext.Path, ext.ExtensionNodes, false);
                        }
                  }
            }
            
            void AddChildExtensions (AddinDescription conf, ModuleDescription module, AddinUpdateData updateData, string path, ExtensionNodeDescriptionCollection nodes, bool conditionChildren)
            {
                  // Don't register conditions as extension nodes.
                  if (!conditionChildren)
                        updateData.RegisterExtension (conf, module, path);
                  
                  foreach (ExtensionNodeDescription node in nodes) {
                        if (node.NodeName == "ComplexCondition")
                              continue;
                        updateData.RelExtensionNodes++;
                        string id = node.GetAttribute ("id");
                        if (id.Length != 0)
                              AddChildExtensions (conf, module, updateData, path + "/" + id, node.ChildNodes, node.NodeName == "Condition");
                  }
            }
            
            string[] GetDomains ()
            {
                  string[] dirs = fileDatabase.GetDirectories (AddinCachePath);
                  string[] ids = new string [dirs.Length];
                  for (int n=0; n<dirs.Length; n++)
                        ids [n] = Path.GetFileName (dirs [n]);
                  return ids;
            }

            public string GetUniqueDomainId ()
            {
                  if (lastDomainId != 0) {
                        lastDomainId++;
                        return lastDomainId.ToString ();
                  }
                  lastDomainId = 1;
                  foreach (string s in fileDatabase.GetDirectories (AddinCachePath)) {
                        string dn = Path.GetFileName (s);
                        if (dn == GlobalDomain)
                              continue;
                        try {
                              int n = int.Parse (dn);
                              if (n >= lastDomainId)
                                    lastDomainId = n + 1;
                        } catch {
                        }
                  }
                  return lastDomainId.ToString ();
            }
            
            internal void ResetCachedData ()
            {
                  allSetupInfos = null;
                  addinSetupInfos = null;
                  rootSetupInfos = null;
                  hostIndex = null;
                  cachedAddinSetupInfos.Clear ();
            }
            
            
            public bool AddinDependsOn (string domain, string id1, string id2)
            {
                  Addin addin1 = GetInstalledAddin (domain, id1, false);
                  
                  // We can assumbe that if the add-in is not returned here, it may be a root addin.
                  if (addin1 == null)
                        return false;

                  id2 = Addin.GetIdName (id2);
                  foreach (Dependency dep in addin1.AddinInfo.Dependencies) {
                        AddinDependency adep = dep as AddinDependency;
                        if (adep == null)
                              continue;
                        string depid = Addin.GetFullId (addin1.AddinInfo.Namespace, adep.AddinId, null);
                        if (depid == id2)
                              return true;
                        else if (AddinDependsOn (domain, depid, id2))
                              return true;
                  }
                  return false;
            }
            
            public void Repair (IProgressStatus monitor, string domain)
            {
                  using (fileDatabase.LockWrite ()) {
                        try {
                              if (Directory.Exists (AddinCachePath))
                                    Directory.Delete (AddinCachePath, true);
                              if (Directory.Exists (AddinFolderCachePath))
                                    Directory.Delete (AddinFolderCachePath, true);
                              if (File.Exists (HostIndexFile))
                                    File.Delete (HostIndexFile);
                        }
                        catch (Exception ex) {
                              monitor.ReportError ("The add-in registry could not be rebuilt. It may be due to lack of write permissions to the directory: " + AddinDbDir, ex);
                        }
                  }
                  Update (monitor, domain);
            }
            
            public void Update (IProgressStatus monitor, string domain)
            {
                  if (monitor == null)
                        monitor = new ConsoleProgressStatus (false);

                  if (RunningSetupProcess)
                        return;
                  
                  fatalDatabseError = false;
                  
                  DateTime tim = DateTime.Now;
                  
                  Hashtable installed = new Hashtable ();
                  bool changesFound = CheckFolders (monitor);
                  
                  if (monitor.IsCanceled)
                        return;
                  
                  if (monitor.LogLevel > 1)
                        monitor.Log ("Folders checked (" + (int) (DateTime.Now - tim).TotalMilliseconds + " ms)");
                  
                  if (changesFound) {
                        // Something has changed, the add-ins need to be re-scanned, but it has
                        // to be done in an external process
                        
                        if (domain != null) {
                              using (fileDatabase.LockRead ()) {
                                    foreach (Addin ainfo in InternalGetInstalledAddins (domain, AddinType.Addin)) {
                                          installed [ainfo.Id] = ainfo.Id;
                                    }
                              }
                        }
                        
                        RunScannerProcess (monitor);
                  
                        ResetCachedData ();
                        
                        registry.NotifyDatabaseUpdated ();
                  }
                  
                  if (fatalDatabseError)
                        monitor.ReportError ("The add-in database could not be updated. It may be due to file corruption. Try running the setup repair utility", null);
                  
                  // Update the currently loaded add-ins
                  if (changesFound && domain != null && AddinManager.IsInitialized && AddinManager.Registry.RegistryPath == registry.RegistryPath) {
                        Hashtable newInstalled = new Hashtable ();
                        foreach (Addin ainfo in GetInstalledAddins (domain, AddinType.Addin)) {
                              newInstalled [ainfo.Id] = ainfo.Id;
                        }
                        
                        foreach (string aid in installed.Keys) {
                              if (!newInstalled.Contains (aid))
                                    AddinManager.SessionService.UnloadAddin (aid);
                        }
                        
                        foreach (string aid in newInstalled.Keys) {
                              if (!installed.Contains (aid)) {
                                    AddinManager.SessionService.ActivateAddin (aid);
                              }
                        }
                  }
            }
            
            void RunScannerProcess (IProgressStatus monitor)
            {
                  IProgressStatus scanMonitor = monitor;
                  ArrayList pparams = new ArrayList ();
                  pparams.Add (null); // scan folder
                  
                  bool retry = false;
                  do {
                        try {
                              if (monitor.LogLevel > 1)
                                    monitor.Log ("Looking for addins");
                              SetupProcess.ExecuteCommand (scanMonitor, registry.RegistryPath, AddinManager.StartupDirectory, "scan", (string[]) pparams.ToArray (typeof(string)));
                              retry = false;
                        }
                        catch (Exception ex) {
                              ProcessFailedException pex = ex as ProcessFailedException;
                              if (pex != null) {
                                    // Get the last logged operation.
                                    if (pex.LastLog.StartsWith ("scan:")) {
                                          // It crashed while scanning a file. Add the file to the ignore list and try again.
                                          string file = pex.LastLog.Substring (5);
                                          pparams.Add (file);
                                          monitor.ReportWarning ("Could not scan file: " + file);
                                          retry = true;
                                          continue;
                                    }
                              }
                              fatalDatabseError = true;
                              // If the process has crashed, try to do a new scan, this time using verbose log,
                              // to give the user more information about the origin of the crash.
                              if (pex != null && !retry) {
                                    monitor.ReportError ("Add-in scan operation failed. The Mono runtime may have encountered an error while trying to load an assembly.", null);
                                    if (monitor.LogLevel <= 1) {
                                          // Re-scan again using verbose log, to make it easy to find the origin of the error.
                                          retry = true;
                                          scanMonitor = new ConsoleProgressStatus (true);
                                    }
                              } else
                                    retry = false;
                              
                              if (!retry) {
                                    monitor.ReportError ("Add-in scan operation failed", (ex is ProcessFailedException ? null : ex));
                                    monitor.Cancel ();
                                    return;
                              }
                        }
                  }
                  while (retry);
            }
            
            bool DatabaseInfrastructureCheck (IProgressStatus monitor)
            {
                  // Do some sanity check, to make sure the basic database infrastructure can be created
                  
                  bool hasChanges = false;
                  
                  try {
                  
                        if (!Directory.Exists (AddinCachePath)) {
                              Directory.CreateDirectory (AddinCachePath);
                              hasChanges = true;
                        }
                  
                        if (!Directory.Exists (AddinFolderCachePath)) {
                              Directory.CreateDirectory (AddinFolderCachePath);
                              hasChanges = true;
                        }
                  
                        // Make sure we can write in those folders

                        Util.CheckWrittableFloder (AddinCachePath);
                        Util.CheckWrittableFloder (AddinFolderCachePath);
                        
                        fatalDatabseError = false;
                  }
                  catch (Exception ex) {
                        monitor.ReportError ("Add-in cache directory could not be created", ex);
                        fatalDatabseError = true;
                        monitor.Cancel ();
                  }
                  return hasChanges;
            }
            
            
            internal bool CheckFolders (IProgressStatus monitor)
            {
                  using (fileDatabase.LockRead ()) {
                        AddinScanResult scanResult = new AddinScanResult ();
                        scanResult.CheckOnly = true;
                        InternalScanFolders (monitor, scanResult);
                        return scanResult.ChangesFound;
                  }
            }
            
            internal void ScanFolders (IProgressStatus monitor, string folderToScan, StringCollection filesToIgnore)
            {
                  AddinScanResult res = new AddinScanResult ();
                  res.FilesToIgnore = filesToIgnore;
                  ScanFolders (monitor, res);
            }
            
            internal void ScanFolders (IProgressStatus monitor, AddinScanResult scanResult)
            {
                  IDisposable checkLock = null;
                  
                  if (scanResult.CheckOnly)
                        checkLock = fileDatabase.LockRead ();
                  else {
                        // All changes are done in a transaction, which won't be committed until
                        // all files have been updated.
                        
                        if (!fileDatabase.BeginTransaction ()) {
                              // The database is already being updated. Can't do anything for now.
                              return;
                        }
                  }
                  
                  EventInfo einfo = typeof(AppDomain).GetEvent ("ReflectionOnlyAssemblyResolve");
                  ResolveEventHandler resolver = new ResolveEventHandler (OnResolveAddinAssembly);
                  
                  try
                  {
                        // Perform the add-in scan
                        
                        if (!scanResult.CheckOnly) {
                              AppDomain.CurrentDomain.AssemblyResolve += resolver;
                              if (einfo != null) einfo.AddEventHandler (AppDomain.CurrentDomain, resolver);
                        }
                        
                        InternalScanFolders (monitor, scanResult);
                        
                        if (!scanResult.CheckOnly)
                              fileDatabase.CommitTransaction ();
                  }
                  catch {
                        if (!scanResult.CheckOnly)
                              fileDatabase.RollbackTransaction ();
                        throw;
                  }
                  finally {
                        currentScanResult = null;
                        
                        if (scanResult.CheckOnly)
                              checkLock.Dispose ();
                        else {
                              AppDomain.CurrentDomain.AssemblyResolve -= resolver;
                              if (einfo != null) einfo.RemoveEventHandler (AppDomain.CurrentDomain, resolver);
                        }
                  }
            }
            
            void InternalScanFolders (IProgressStatus monitor, AddinScanResult scanResult)
            {
                  DateTime tim = DateTime.Now;
                  
                  DatabaseInfrastructureCheck (monitor);
                  if (monitor.IsCanceled)
                        return;
                  
                  try {
                        scanResult.HostIndex = GetAddinHostIndex ();
                  }
                  catch (Exception ex) {
                        if (scanResult.CheckOnly) {
                              scanResult.ChangesFound = true;
                              return;
                        }
                        monitor.ReportError ("Add-in root index is corrupt. The add-in database will be regenerated.", ex);
                        scanResult.RegenerateAllData = true;
                  }
                  
                  AddinScanner scanner = new AddinScanner (this);
                  
                  // Check if any of the previously scanned folders has been deleted
                  
                  foreach (string file in Directory.GetFiles (AddinFolderCachePath, "*.data")) {
                        AddinScanFolderInfo folderInfo;
                        bool res = ReadFolderInfo (monitor, file, out folderInfo);
                        if (!res || !Directory.Exists (folderInfo.Folder)) {
                              if (res) {
                                    // Folder has been deleted. Remove the add-ins it had.
                                    scanner.UpdateDeletedAddins (monitor, folderInfo, scanResult);
                              }
                              else {
                                    // Folder info file corrupt. Regenerate all.
                                    scanResult.ChangesFound = true;
                                    scanResult.RegenerateRelationData = true;
                              }
                              
                              if (!scanResult.CheckOnly)
                                    SafeDelete (monitor, file);
                              else
                                    return;
                        }
                  }
                  
                  // Look for changes in the add-in folders
                  
                  foreach (string dir in registry.AddinDirectories) {
                        if (dir == registry.DefaultAddinsFolder)
                              scanner.ScanFolderRec (monitor, dir, GlobalDomain, scanResult);
                        else
                              scanner.ScanFolder (monitor, dir, GlobalDomain, scanResult);
                        if (scanResult.CheckOnly) {
                              if (scanResult.ChangesFound || monitor.IsCanceled)
                                    return;
                        }
                  }
                  
                  if (scanResult.CheckOnly)
                        return;
                  
                  // Scan the files which have been modified
                  
                  currentScanResult = scanResult;

                  foreach (FileToScan file in scanResult.FilesToScan)
                        scanner.ScanFile (monitor, file.File, file.AddinScanFolderInfo, scanResult);

                  // Save folder info
                  
                  foreach (AddinScanFolderInfo finfo in scanResult.ModifiedFolderInfos)
                        SaveFolderInfo (monitor, finfo);

                  if (monitor.LogLevel > 1)
                        monitor.Log ("Folders scan completed (" + (int) (DateTime.Now - tim).TotalMilliseconds + " ms)");

                  SaveAddinHostIndex ();
                  ResetCachedData ();
                  
                  if (!scanResult.ChangesFound) {
                        if (monitor.LogLevel > 1)
                              monitor.Log ("No changes found");
                        return;
                  }
                  
                  tim = DateTime.Now;
                  try {
                        if (scanResult.RegenerateRelationData)
                              scanResult.AddinsToUpdateRelations = null;
                        
                        GenerateAddinExtensionMapsInternal (monitor, scanResult.AddinsToUpdateRelations, scanResult.RemovedAddins);
                  }
                  catch (Exception ex) {
                        fatalDatabseError = true;
                        monitor.ReportError ("The add-in database could not be updated. It may be due to file corruption. Try running the setup repair utility", ex);
                  }
                  
                  if (monitor.LogLevel > 1)
                        monitor.Log ("Add-in relations analyzed (" + (int) (DateTime.Now - tim).TotalMilliseconds + " ms)");
                  
                  SaveAddinHostIndex ();
            }
            
            public void ParseAddin (IProgressStatus progressStatus, string file, string outFile, bool inProcess)
            {
                  if (!inProcess) {
                        SetupProcess.ExecuteCommand (progressStatus, registry.RegistryPath, AddinManager.StartupDirectory, "get-desc", Path.GetFullPath (file), outFile);
                        return;
                  }
                  
                  using (fileDatabase.LockRead ())
                  {
                        // First of all, check if the file belongs to a registered add-in
                        AddinScanFolderInfo finfo;
                        if (GetFolderInfoForPath (progressStatus, Path.GetDirectoryName (file), out finfo) && finfo != null) {
                              AddinFileInfo afi = finfo.GetAddinFileInfo (file);
                              if (afi != null && afi.IsAddin) {
                                    AddinDescription adesc;
                                    GetAddinDescription (progressStatus, afi.Domain, afi.AddinId, out adesc);
                                    if (adesc != null)
                                          adesc.Save (outFile);
                                    return;
                              }
                        }
                        
                        
                        AddinScanner scanner = new AddinScanner (this);
                        
                        SingleFileAssemblyResolver res = new SingleFileAssemblyResolver (progressStatus, registry, scanner);
                        ResolveEventHandler resolver = new ResolveEventHandler (res.Resolve);

                        EventInfo einfo = typeof(AppDomain).GetEvent ("ReflectionOnlyAssemblyResolve");
                        
                        try {
                              AppDomain.CurrentDomain.AssemblyResolve += resolver;
                              if (einfo != null) einfo.AddEventHandler (AppDomain.CurrentDomain, resolver);
                        
                              AddinDescription desc = scanner.ScanSingleFile (progressStatus, file, new AddinScanResult ());
                              if (desc != null)
                                    desc.Save (outFile);
                        }
                        finally {
                              AppDomain.CurrentDomain.AssemblyResolve -= resolver;
                              if (einfo != null) einfo.RemoveEventHandler (AppDomain.CurrentDomain, resolver);
                        }
                  }
            }
            
            public string GetFolderDomain (IProgressStatus progressStatus, string path)
            {
                  AddinScanFolderInfo folderInfo;
                  if (GetFolderInfoForPath (progressStatus, path, out folderInfo) && folderInfo != null && !folderInfo.SharedFolder)
                        return folderInfo.Domain;
                  else
                        return GlobalDomain;
            }
            
            Assembly OnResolveAddinAssembly (object s, ResolveEventArgs args)
            {
                  string file = currentScanResult.GetAssemblyLocation (args.Name);
                  if (file != null)
                        return Util.LoadAssemblyForReflection (file);
                  else {
                        Console.WriteLine ("Assembly not found: " + args.Name);
                        return null;
                  }
            }
            
            public string GetFolderConfigFile (string path)
            {
                  path = Path.GetFullPath (path);
                  
                  string s = path.Replace ("_", "__");
                  s = s.Replace (Path.DirectorySeparatorChar, '_');
                  s = s.Replace (Path.AltDirectorySeparatorChar, '_');
                  s = s.Replace (Path.VolumeSeparatorChar, '_');
                  
                  return Path.Combine (AddinFolderCachePath, s + ".data");
            }
            
            internal void UninstallAddin (IProgressStatus monitor, string domain, string addinId, AddinScanResult scanResult)
            {
                  scanResult.AddRemovedAddin (addinId);
                  string file = GetDescriptionPath (domain, addinId);
                  if (!fileDatabase.Exists (file)) {
                        return;
                  }
                  
                  // Add-in already existed. The dependencies of the old add-in need to be re-analized

                  AddinDescription desc;
                  if (ReadAddinDescription (monitor, file, out desc)) {
                        Util.AddDependencies (desc, scanResult);
                        if (desc.IsRoot)
                              scanResult.HostIndex.RemoveHostData (desc.AddinId, desc.AddinFile);
                  } else
                        // If we can't get information about the old assembly, just regenerate all relation data
                        scanResult.RegenerateRelationData = true;

                  SafeDelete (monitor, file);
                  string dir = Path.GetDirectoryName (file);
                  if (fileDatabase.DirectoryIsEmpty (dir))
                        SafeDeleteDir (monitor, dir);
                  SafeDeleteDir (monitor, Path.Combine (AddinPrivateDataPath, Path.GetFileNameWithoutExtension (file)));
            }
            
            public bool GetAddinDescription (IProgressStatus monitor, string domain, string addinId, out AddinDescription description)
            {
                  string file = GetDescriptionPath (domain, addinId);
                  return ReadAddinDescription (monitor, file, out description);
            }
            
            public bool ReadAddinDescription (IProgressStatus monitor, string file, out AddinDescription description)
            {
                  try {
                        description = AddinDescription.ReadBinary (fileDatabase, file);
                        if (description != null)
                              description.OwnerDatabase = this;
                        return true;
                  }
                  catch (Exception ex) {
                        if (monitor == null)
                              throw;
                        description = null;
                        monitor.ReportError ("Could not read folder info file", ex);
                        return false;
                  }
            }
            
            public bool SaveDescription (IProgressStatus monitor, AddinDescription desc, string replaceFileName)
            {
                  try {
                        if (replaceFileName != null)
                              desc.SaveBinary (fileDatabase, replaceFileName);
                        else {
                              string file = GetDescriptionPath (desc.Domain, desc.AddinId);
                              string dir = Path.GetDirectoryName (file);
                              if (!fileDatabase.DirExists (dir))
                                    fileDatabase.CreateDir (dir);
                              desc.SaveBinary (fileDatabase, file);
                        }
                        return true;
                  }
                  catch (Exception ex) {
                        monitor.ReportError ("Add-in info file could not be saved", ex);
                        return false;
                  }
            }
            
            public bool AddinDescriptionExists (string domain, string addinId)
            {
                  string file = GetDescriptionPath (domain, addinId);
                  return fileDatabase.Exists (file);
            }
            
            public bool ReadFolderInfo (IProgressStatus monitor, string file, out AddinScanFolderInfo folderInfo)
            {
                  try {
                        folderInfo = AddinScanFolderInfo.Read (fileDatabase, file);
                        return true;
                  }
                  catch (Exception ex) {
                        folderInfo = null;
                        monitor.ReportError ("Could not read folder info file", ex);
                        return false;
                  }
            }
            
            public bool GetFolderInfoForPath (IProgressStatus monitor, string path, out AddinScanFolderInfo folderInfo)
            {
                  try {
                        folderInfo = AddinScanFolderInfo.Read (fileDatabase, AddinFolderCachePath, path);
                        return true;
                  }
                  catch (Exception ex) {
                        folderInfo = null;
                        if (monitor != null)
                              monitor.ReportError ("Could not read folder info file", ex);
                        return false;
                  }
            }

            public bool SaveFolderInfo (IProgressStatus monitor, AddinScanFolderInfo folderInfo)
            {
                  try {
                        folderInfo.Write (fileDatabase, AddinFolderCachePath);
                        return true;
                  }
                  catch (Exception ex) {
                        monitor.ReportError ("Could not write folder info file", ex);
                        return false;
                  }
            }
            
            public bool DeleteFolderInfo (IProgressStatus monitor, AddinScanFolderInfo folderInfo)
            {
                  return SafeDelete (monitor, folderInfo.FileName);
            }
            
            public bool SafeDelete (IProgressStatus monitor, string file)
            {
                  try {
                        fileDatabase.Delete (file);
                        return true;
                  }
                  catch (Exception ex) {
                        if (monitor.LogLevel > 1) {
                              monitor.Log ("Could not delete file: " + file);
                              monitor.Log (ex.ToString ());
                        }
                        return false;
                  }
            }
            
            public bool SafeDeleteDir (IProgressStatus monitor, string dir)
            {
                  try {
                        fileDatabase.DeleteDir (dir);
                        return true;
                  }
                  catch (Exception ex) {
                        if (monitor.LogLevel > 1) {
                              monitor.Log ("Could not delete directory: " + dir);
                              monitor.Log (ex.ToString ());
                        }
                        return false;
                  }
            }
            
            AddinHostIndex GetAddinHostIndex ()
            {
                  if (hostIndex != null)
                        return hostIndex;
                  
                  using (fileDatabase.LockRead ()) {
                        if (fileDatabase.Exists (HostIndexFile))
                              hostIndex = AddinHostIndex.Read (fileDatabase, HostIndexFile);
                        else
                              hostIndex = new AddinHostIndex ();
                  }
                  return hostIndex;
            }
            
            void SaveAddinHostIndex ()
            {
                  if (hostIndex != null)
                        hostIndex.Write (fileDatabase, HostIndexFile);
            }
            
            internal string GetUniqueAddinId (string file, string oldId, string ns, string version)
            {
                  string baseId = "__" + Path.GetFileNameWithoutExtension (file);

                  if (Path.GetExtension (baseId) == ".addin")
                        baseId = Path.GetFileNameWithoutExtension (baseId);
                  
                  string name = baseId;
                  string id = Addin.GetFullId (ns, name, version);
                  
                  // If the old Id is already an automatically generated one, reuse it
                  if (oldId != null && oldId.StartsWith (id))
                        return name;
                  
                  int n = 1;
                  while (AddinIdExists (id)) {
                        name = baseId + "_" + n;
                        id = Addin.GetFullId (ns, name, version);
                        n++;
                  }
                  return name;
            }
            
            bool AddinIdExists (string id)
            {
                  foreach (string d in fileDatabase.GetDirectories (AddinCachePath)) {
                        if (fileDatabase.Exists (Path.Combine (d, id + ".addin")))
                            return true;
                  }
                  return false;
            }
            
            public void ResetConfiguration ()
            {
                  if (File.Exists (ConfigFile))
                        File.Delete (ConfigFile);
            }
            
            DatabaseConfiguration Configuration {
                  get {
                        if (config == null) {
                              using (fileDatabase.LockRead ()) {
                                    if (fileDatabase.Exists (ConfigFile))
                                          config = DatabaseConfiguration.Read (ConfigFile);
                                    else
                                          config = new DatabaseConfiguration ();
                              }
                        }
                        return config;
                  }
            }
            
            void SaveConfiguration ()
            {
                  if (config != null) {
                        using (fileDatabase.LockWrite ()) {
                              config.Write (ConfigFile);
                        }
                  }
            }
      }
      
      class SingleFileAssemblyResolver
      {
            AddinScanResult scanResult;
            AddinScanner scanner;
            AddinRegistry registry;
            IProgressStatus progressStatus;
            
            public SingleFileAssemblyResolver (IProgressStatus progressStatus, AddinRegistry registry, AddinScanner scanner)
            {
                  this.scanner = scanner;
                  this.registry = registry;
                  this.progressStatus = progressStatus;
            }
            
            public Assembly Resolve (object s, ResolveEventArgs args)
            {
                  if (scanResult == null) {
                        scanResult = new AddinScanResult ();
                        scanResult.LocateAssembliesOnly = true;
                  
                        foreach (string dir in registry.AddinDirectories)
                              scanner.ScanFolder (progressStatus, dir, AddinDatabase.GlobalDomain, scanResult);
                  }
            
                  string afile = scanResult.GetAssemblyLocation (args.Name);
                  if (afile != null)
                        return Util.LoadAssemblyForReflection (afile);
                  else
                        return null;
            }
      }
      
      class AddinIndex
      {
            Hashtable addins = new Hashtable ();
            
            public void Add (AddinDescription desc)
            {
                  string id = Addin.GetFullId (desc.Namespace, desc.LocalId, null);
                  ArrayList list = (ArrayList) addins [id];
                  if (list == null) {
                        list = new ArrayList (); 
                        addins [id] = list;
                  }
                  list.Add (desc);
            }
            
            ArrayList FindDescriptions (string domain, string fullid)
            {
                  // Returns all registered add-ins which are compatible with the provided
                  // fullid. Compatible means that the id is the same and the version is within
                  // the range of compatible versions of the add-in.
                  
                  ArrayList res = new ArrayList ();
                  string id = Addin.GetIdName (fullid);
                  ArrayList list = (ArrayList) addins [id];
                  if (list == null)
                        return res;
                  string version = Addin.GetIdVersion (fullid);
                  foreach (AddinDescription desc in list) {
                        if ((desc.Domain == domain || domain == AddinDatabase.GlobalDomain) && desc.SupportsVersion (version))
                              res.Add (desc);
                  }
                  return res;
            }
            
            public ArrayList GetSortedAddins ()
            {
                  Hashtable inserted = new Hashtable ();
                  Hashtable lists = new Hashtable ();
                  
                  foreach (ArrayList dlist in addins.Values) {
                        foreach (AddinDescription desc in dlist)
                              InsertSortedAddin (inserted, lists, desc);
                  }
                  
                  // Merge all domain lists into a single list.
                  // Make sure the global domain is inserted the last
                  
                  ArrayList global = (ArrayList) lists [AddinDatabase.GlobalDomain];
                  lists.Remove (AddinDatabase.GlobalDomain);
                  
                  ArrayList list = new ArrayList ();
                  foreach (ArrayList dl in lists.Values) {
                        list.AddRange (dl);
                  }
                  if (global != null)
                        list.AddRange (global);
                  return list;
            }

            void InsertSortedAddin (Hashtable inserted, Hashtable lists, AddinDescription desc)
            {
                  string sid = desc.AddinId + " " + desc.Domain;
                  if (inserted.ContainsKey (sid))
                        return;
                  inserted [sid] = desc;
                  foreach (ModuleDescription mod in desc.AllModules) {
                        foreach (Dependency dep in mod.Dependencies) {
                              AddinDependency adep = dep as AddinDependency;
                              if (adep == null)
                                    continue;
                              ArrayList descs = FindDescriptions (desc.Domain, adep.FullAddinId);
                              if (descs.Count > 0) {
                                    foreach (AddinDescription sd in descs)
                                          InsertSortedAddin (inserted, lists, sd);
                              }
//                            else 
//                                  Console.WriteLine ("NOT FOUND: " + adep.FullAddinId + " " + desc.Domain + " from " + sid);
                        }
                  }
                  ArrayList list = (ArrayList) lists [desc.Domain];
                  if (list == null) {
                        list = new ArrayList ();
                        lists [desc.Domain] = list;
                  }
                  
                  list.Add (desc);
            }
      }
      
      enum AddinType
      {
            Addin,
            Root,
            All
      }
}



Generated by  Doxygen 1.6.0   Back to index