source: trunk/CrypCore/PluginManager.cs @ 2511

Last change on this file since 2511 was 2511, checked in by Sven Rech, 11 years ago

plugin loading can be disabled in settings now

File size: 14.9 KB
Line 
1/*
2   Copyright 2008 Martin Saternus, University of Duisburg-Essen
3
4   Licensed under the Apache License, Version 2.0 (the "License");
5   you may not use this file except in compliance with the License.
6   You may obtain a copy of the License at
7
8       http://www.apache.org/licenses/LICENSE-2.0
9
10   Unless required by applicable law or agreed to in writing, software
11   distributed under the License is distributed on an "AS IS" BASIS,
12   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   See the License for the specific language governing permissions and
14   limitations under the License.
15*/
16
17using System;
18using System.Collections;
19using System.Collections.Generic;
20using System.Reflection;
21using System.IO;
22
23namespace Cryptool.Core
24{
25    /// <summary>
26    /// PluginManager Class 
27    /// </summary>
28    public class PluginManager
29    {
30        private readonly HashSet<string> disabledAssemblies = new HashSet<string>();
31
32        /// <summary>
33        /// Counter for the dll files that were found
34        /// </summary>
35        private int availablePluginsApproximation = 0;
36
37        /// <summary>
38        /// Subdirectory to store plugins
39        /// </summary>
40        private const string PluginDirecory = "CrypPlugins";
41
42        /// <summary>
43        /// Fires if an exception occurs
44        /// </summary>
45        public event CrypCoreExceptionEventHandler OnExceptionOccured;
46
47        /// <summary>
48        /// Fires if an info occurs
49        /// </summary>
50        public event CrypCoreDebugEventHandler OnDebugMessageOccured;
51
52        /// <summary>
53        /// Occurs when a plugin was loaded
54        /// </summary>
55        public event CrypCorePluginLoadedHandler OnPluginLoaded;
56       
57        /// <summary>
58        /// Custom Plugin Store Directory
59        /// </summary>
60        private readonly string customPluginStore;
61
62        /// <summary>
63        /// Global Plugin Store Directory
64        /// </summary>
65        private readonly string globalPluginStore;
66
67        /// <summary>
68        /// Loaded Assemblies
69        /// </summary>
70        private readonly Dictionary<string, Assembly> loadedAssemblies;
71
72        /// <summary>
73        /// Loaded Types
74        /// </summary>
75        private readonly Dictionary<string, Type> loadedTypes;
76
77        Dictionary<string, Assembly> foundAssemblies = new Dictionary<string, Assembly>();
78       
79        /// <summary>
80        /// cTor
81        /// </summary>
82        public PluginManager(HashSet<string> disabledAssemblies)
83        {
84            this.disabledAssemblies = disabledAssemblies;
85           
86            this.customPluginStore = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), PluginDirecory);
87            this.globalPluginStore = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), PluginDirecory);
88            this.loadedAssemblies = new Dictionary<string, Assembly>();
89            this.loadedTypes = new Dictionary<string, Type>();         
90        }
91
92        /// <summary>
93        /// Returns a type of a loaded assembly
94        /// </summary>
95        /// <param name="assemblyName">Assembly Name</param>
96        /// <param name="typeName">Type Name</param>
97        /// <returns>Return the type or null if no type could be found</returns>
98        public Type LoadType(string assemblyName, string typeName)
99        {
100            if (this.loadedAssemblies.ContainsKey(assemblyName))
101                return this.loadedAssemblies[assemblyName].GetType(typeName, false);
102            return null;
103        }
104
105        /// <summary>
106        /// Returns all types found in the plugins
107        /// </summary>
108        /// <param name="state">Load type from all plugins or from signed only</param>
109        /// <returns></returns>
110        public Dictionary<string, Type> LoadTypes(AssemblySigningRequirement state)
111        {
112            if (Directory.Exists(globalPluginStore))
113            {
114              availablePluginsApproximation = AvailablePluginsApproximation(new DirectoryInfo(globalPluginStore));
115              FindAssemblies(new DirectoryInfo(globalPluginStore), state, foundAssemblies);
116            }
117
118            // custom plugin store is not supported yet
119            //if (!Directory.Exists(customPluginStore))
120            //    Directory.CreateDirectory(customPluginStore);
121            //FindAssemblies(new DirectoryInfo(customPluginStore), state, foundAssemblies);
122            LoadTypes(foundAssemblies);
123            return this.loadedTypes;
124        }
125
126
127        [Obsolete("will be removed soon")]
128        private int AvailablePluginsApproximation(DirectoryInfo directory)
129        {
130          int count = 0;
131          foreach (DirectoryInfo subDirectory in directory.GetDirectories())
132          {
133            count = AvailablePluginsApproximation(subDirectory);
134          }
135          return directory.GetFiles("*.dll").Length;
136        }
137
138        /// <summary>
139        /// Search all subdirectories for assemblies
140        /// </summary>
141        /// <param name="directory">Root directory</param>
142        /// <param name="state">Search for all or only for signed assemblies</param>
143        /// <param name="foundAssemblies">list of found assemblies</param>
144        private void FindAssemblies(DirectoryInfo directory, AssemblySigningRequirement state, Dictionary<string, Assembly> foundAssemblies)
145        {
146            foreach (DirectoryInfo subDirectory in directory.GetDirectories())
147            {
148                FindAssemblies(subDirectory, state, foundAssemblies);
149            }
150
151            int currentPosition = 0;
152            foreach (FileInfo fileInfo in directory.GetFiles("*.dll"))
153            {
154                if (disabledAssemblies != null && disabledAssemblies.Contains(fileInfo.Name))
155                    continue;
156
157                currentPosition++;
158                try
159                {
160                    Assembly asm = Assembly.Load(AssemblyName.GetAssemblyName(fileInfo.FullName));
161                   
162                    string key = GetAssemblyKey(asm.FullName, state);
163                    if (key == null)
164                        throw new UnknownFileFormatException(fileInfo.FullName);
165
166                    bool sendMessage = false;
167                    if (!foundAssemblies.ContainsKey(key))
168                    {
169                      foundAssemblies.Add(key, asm);
170                      sendMessage = true;
171                    }
172                    else
173                      if (new AssemblyName(asm.FullName).Version > new AssemblyName(foundAssemblies[key].FullName).Version)
174                      {
175                        foundAssemblies[key] = asm;
176                        sendMessage = true;
177                      }
178
179                    if (sendMessage)
180                    {
181                        SendDebugMessage("Loaded Assembly \"" + asm.FullName + "\" from file: " + fileInfo.FullName);
182                        if (OnPluginLoaded != null)
183                        {
184                          OnPluginLoaded(this, new PluginLoadedEventArgs(currentPosition, this.availablePluginsApproximation, asm.GetName().Name + " Version=" + asm.GetName().Version.ToString()));
185                        }                         
186                    }
187                }
188                catch (BadImageFormatException)
189                {
190                  SendExceptionMessage(string.Format(Resources.Exceptions.non_plugin_file, fileInfo.Name));
191                }
192                catch (Exception ex)
193                {
194                    SendExceptionMessage(ex);
195                }
196            }
197        }
198
199        /// <summary>
200        /// Interate the found assemblies and add well known types
201        /// </summary>
202        /// <param name="foundAssemblies">list of found assemblies</param>
203        private void LoadTypes(Dictionary<string, Assembly> foundAssemblies)
204        {
205            string interfaceName = "Cryptool.PluginBase.IPlugin";
206
207            foreach (Assembly asm in foundAssemblies.Values)
208            {
209                AssemblyName assemblyName = new AssemblyName(asm.FullName);
210                try
211                {
212                  foreach (Type type in asm.GetTypes())
213                  {
214                    if (type.GetInterface(interfaceName) != null && !this.loadedTypes.ContainsKey(type.FullName))
215                    {
216                      this.loadedTypes.Add(type.FullName, type);
217                      if (!this.loadedAssemblies.ContainsKey(assemblyName.Name))
218                      {
219                        this.loadedAssemblies.Add(assemblyName.Name, asm);
220                      }
221                    }
222                  }
223                }
224                catch (ReflectionTypeLoadException tle)
225                {
226                  if (OnExceptionOccured != null)
227                  {
228                    OnExceptionOccured(this, new PluginManagerEventArgs(new TypeLoadException(asm.FullName + "\n" + tle.LoaderExceptions[0].Message)));
229                  }
230                }
231                catch (Exception exception)
232                {
233                  if (OnExceptionOccured != null)
234                  {
235                    OnExceptionOccured(this, new PluginManagerEventArgs(new TypeLoadException(asm.FullName + "\n" + exception.Message)));
236                  }
237                }
238            }
239        }
240
241        /// <summary>
242       /// Create a unique key for each assembly
243       /// </summary>
244       /// <param name="assemblyFullName">Full name of the assembly</param>
245       /// <param name="state">Signed or unsigned</param>
246       /// <returns>Returns the key or null if public key is null and signing is required</returns>
247        private string GetAssemblyKey(string assemblyFullName, AssemblySigningRequirement state)
248        {
249            AssemblyName asmName = new AssemblyName(assemblyFullName);
250            if (state == AssemblySigningRequirement.LoadSignedAssemblies)
251            {
252                if (asmName.KeyPair.PublicKey == null)
253                    return null;
254                return asmName.Name + "__" + asmName.KeyPair.ToString();
255            }
256            return asmName.Name;
257        }
258
259        /// <summary>
260        /// Adds an assembly to the store
261        /// </summary>
262        /// <param name="buffer">byte[] of the assembly</param>
263        /// <param name="state">Signed or unsigned</param>
264        /// <param name="pluginStore">Global or Custom Store</param>
265        public void AddPlugin(byte[] buffer, AssemblySigningRequirement state, PluginStore pluginStore)
266        {
267            try
268            {
269                Assembly asm = Assembly.ReflectionOnlyLoad(buffer);
270                AssemblyName asmName = new AssemblyName(asm.FullName);
271
272                string publicKeyToken = GetPublicToken(asm);
273                if ((state == AssemblySigningRequirement.StoreSignedAssemblies) && (publicKeyToken == string.Empty))
274                    throw new AssemblyNotSignedException();
275
276                string pluginStoreDirectory = GetPluginDirectory(asmName, publicKeyToken, pluginStore);
277                if (!Directory.Exists(pluginStoreDirectory))
278                    Directory.CreateDirectory(pluginStoreDirectory);
279
280                WriteFile(buffer, pluginStoreDirectory, asmName);
281            }
282            catch (AssemblyNotSignedException ex)
283            {
284                if (OnExceptionOccured != null)
285                    OnExceptionOccured(this, new PluginManagerEventArgs(ex));
286            }
287            catch
288            {
289                if (OnExceptionOccured != null)
290                    OnExceptionOccured(this, new PluginManagerEventArgs(new StoreAddingException()));
291            }
292        }
293
294        /// <summary>
295        /// Writes the buffer to disk
296        /// </summary>
297        /// <param name="buffer">byte[] of the assembly</param>
298        /// <param name="pluginStoreDirectory">Directory to store the assembly</param>
299        /// <param name="assemblyName">Name of the assembly</param>
300        private void WriteFile(byte[] buffer, string pluginStoreDirectory, AssemblyName assemblyName)
301        {
302            string assemblyFileName = Path.Combine(pluginStoreDirectory, assemblyName.Name + ".dll");
303            if (!File.Exists(assemblyFileName))
304            {
305                FileStream fs = File.Create(assemblyFileName);
306                BinaryWriter bw = new BinaryWriter(fs);
307                bw.Write(buffer);
308                bw.Close();
309                fs.Close();
310            }
311        }
312
313        /// <summary>
314        /// Get the directory for the assembly
315        /// </summary>
316        /// <param name="assemblyName">Name of the assembly</param>
317        /// <param name="publicKeyToken">Public token</param>
318        /// <param name="pluginStore">Global or Custom Store</param>
319        /// <returns>Returns the full directory name</returns>
320        private string GetPluginDirectory(AssemblyName assemblyName, string publicKeyToken, PluginStore pluginStore)
321        {
322            string versionName = string.Empty;
323            if (publicKeyToken == String.Empty)
324                versionName = Path.Combine(assemblyName.Name, assemblyName.Version.ToString());
325            else
326                versionName = Path.Combine(assemblyName.Name, assemblyName.Version.ToString() + "__" + publicKeyToken);
327
328            switch (pluginStore)
329            {
330                case PluginStore.GlobalPluginStore:
331                    return Path.Combine(globalPluginStore, versionName);
332                case PluginStore.CustomPluginStore:
333                    return Path.Combine(customPluginStore, versionName);
334            }
335            return String.Empty;
336        }
337
338        /// <summary>
339        /// Returns the public token
340        /// </summary>
341        /// <param name="asm"></param>
342        /// <returns></returns>
343        private string GetPublicToken(Assembly asm)
344        {
345            byte[] hexBytes = asm.GetName().GetPublicKeyToken();
346            string hexString = String.Empty;
347            for (int i = 0; i < hexBytes.Length; i++)
348            {
349                hexString += hexBytes[i].ToString("x");
350            }
351            return hexString;
352        }
353
354        /// <summary>
355        /// Sends a debug message.
356        /// </summary>
357        /// <param name="message">The message.</param>
358        private void SendDebugMessage(string message)
359        {
360          if (OnDebugMessageOccured != null)
361            OnDebugMessageOccured(this, new PluginManagerEventArgs(message));
362        }
363
364        private void SendExceptionMessage(Exception ex)
365        {
366          if (OnExceptionOccured != null)
367            OnExceptionOccured(this, new PluginManagerEventArgs(ex));
368        }
369
370        private void SendExceptionMessage(string message)
371        {
372          if (OnExceptionOccured != null)
373            OnExceptionOccured(this, new PluginManagerEventArgs(message));
374        }
375    }
376}
Note: See TracBrowser for help on using the repository browser.