2 // Radegast Metaverse Client
3 // Copyright (c) 2009-2012, Radegast Development Team
4 // All rights reserved.
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are met:
9 // * Redistributions of source code must retain the above copyright notice,
10 // this list of conditions and the following disclaimer.
11 // * Redistributions in binary form must reproduce the above copyright
12 // notice, this list of conditions and the following disclaimer in the
13 // documentation and/or other materials provided with the distribution.
14 // * Neither the name of the application "Radegast", nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 using System.Collections.Generic;
34 using System.Reflection;
35 using System.CodeDom.Compiler;
36 using System.Windows.Forms;
38 using Microsoft.CSharp;
43 /// Information about loaded plugin
45 public class PluginInfo
47 /// <summary>File name from which the plugin was loaded, cannot load plugin twice from the same file</summary>
48 public string FileName { get; set; }
49 /// <summary>Plugin class</summary>
50 public IRadegastPlugin Plugin { get; set; }
51 /// <summary>Is plugin started</summary>
52 public bool Started { get; set; }
53 /// <summary>Plugin class</summary>
54 public PluginAttribute Attribures
58 if (Plugin == null) return null;
59 return PluginManager.GetAttributes(Plugin);
65 /// Handles loading Radegast plugins
67 public class PluginManager : IDisposable
69 /// <summary>List of files that should not be scanned for plugins</summary>
70 public static readonly List<string> PluginBlackList = new List<string>(new string[]
74 "Meebey.SmartIrc4net.dll",
77 "OpenMetaverse.Rendering.Meshmerizer.dll",
78 "OpenMetaverse.StructuredData.dll",
80 "OpenMetaverseTypes.dll",
86 "Tao.Platform.Windows.dll",
92 "openjpeg-dotnet-x86_64.dll",
93 "openjpeg-dotnet.dll",
98 /// <summary>List of file extensions that could potentially hold plugins</summary>
99 public static readonly List<string> AllowedPluginExtensions = new List<string>(new string[]
106 List<PluginInfo> PluginsLoaded = new List<PluginInfo>();
107 RadegastInstance instance;
110 /// Gets the list of currently loaded plugins
112 public List<PluginInfo> Plugins
116 return PluginsLoaded;
121 /// Creates new PluginManager
123 /// <param name="instance">Radegast instance PluginManager is associated with</param>
124 public PluginManager(RadegastInstance instance)
126 this.instance = instance;
130 /// Unloads all plugins
132 public void Dispose()
136 List<PluginInfo> unload = new List<PluginInfo>(PluginsLoaded);
137 unload.ForEach(plug =>
147 /// <param name="plug">Plugin to unload</param>
148 public void UnloadPlugin(PluginInfo plug)
152 PluginsLoaded.ForEach((PluginInfo info) =>
154 if (info.FileName == plug.FileName || info.Plugin == plug.Plugin)
156 try { info.Plugin.StopPlugin(instance); }
157 catch (Exception ex) { Logger.Log("ERROR in unloading plugin: " + info.Plugin.GetType().Name + " because " + ex, Helpers.LogLevel.Debug, ex); }
160 PluginsLoaded.RemoveAll((PluginInfo info) => { return plug.FileName == info.FileName || info.Plugin == plug.Plugin; });
165 /// Gets extended atributes for plugin
167 /// <param name="plug">Plugin to lookup extra attributes</param>
168 /// <returns>Extended atributes for plugin</returns>
169 public static PluginAttribute GetAttributes(IRadegastPlugin plug)
171 PluginAttribute a = null;
173 foreach (Attribute attr in Attribute.GetCustomAttributes(plug.GetType()))
175 if (attr is PluginAttribute)
176 a = (PluginAttribute)attr;
181 a = new PluginAttribute();
182 a.Name = plug.GetType().FullName;
189 /// Starts all loaded plugins
191 public void StartPlugins()
195 foreach (PluginInfo plug in PluginsLoaded)
197 Logger.DebugLog("Starting " + plug.Plugin.GetType().FullName);
200 plug.Plugin.StartPlugin(instance);
205 Logger.Log("ERROR in Starting Radegast Plugin: " + plug + " because " + ex, Helpers.LogLevel.Debug);
212 /// Loads a plugin for a precompiled assembly or source file
214 /// <param name="loadFileName">File to load</param>
215 /// <param name="stratPlugins">Start plugins that are found in the assembly</param>
216 public void LoadPluginFile(string loadFileName, bool stratPlugins)
218 string ext = Path.GetExtension(loadFileName).ToLower();
221 LoadCSharpScriptFile(loadFileName, stratPlugins);
223 else if (ext == ".dll" || ext == ".exe")
227 Assembly assembly = Assembly.LoadFile(loadFileName);
228 LoadAssembly(loadFileName, assembly, stratPlugins);
230 catch (BadImageFormatException)
234 catch (ReflectionTypeLoadException)
236 // Out of date or dlls missing sub dependencies
238 catch (TypeLoadException)
240 // Another version of: Out of date or dlls missing sub dependencies
244 Logger.Log("ERROR in Radegast Plugin: " + loadFileName + " because " + ex, Helpers.LogLevel.Debug);
250 /// Scans and load plugins from Radegast application folder without starting them
252 public void ScanAndLoadPlugins()
254 string dirName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
256 if (!Directory.Exists(dirName)) return;
258 foreach (string loadFileName in Directory.GetFiles(dirName))
260 if (IsUnusedFile(loadFileName))
265 LoadPluginFile(loadFileName, false);
269 private static bool IsUnusedFile(string loadFileName)
271 if (!AllowedPluginExtensions.Contains(Path.GetExtension(loadFileName).ToLower())) return true;
272 loadFileName = Path.GetFileName(loadFileName).ToLower();
274 foreach (string blackList in PluginBlackList)
276 if (loadFileName.StartsWith(blackList.ToLower()))
285 /// Loads and compiles a plugin from a C# source file
287 /// <param name="fileName">Load plugin from this filename</param>
288 /// <param name="startPlugins">Start plugins found in the assembly after complilation</param>
289 public void LoadCSharpScriptFile(string fileName, bool startPlugins)
291 try { LoadCSharpScript(fileName, File.ReadAllText(fileName), startPlugins); }
294 Logger.Log("Failed loading C# script " + fileName + ": ", Helpers.LogLevel.Warning, ex);
299 /// Compiles plugin from string source code
301 /// <param name="fileName">File name from which source was loaded</param>
302 /// <param name="code">Source code</param>
303 /// <param name="startPlugins">Start plugins found in the assembly after complilation</param>
304 public void LoadCSharpScript(string fileName, string code, bool startPlugins)
308 // *** Generate dynamic compiler
309 Dictionary<string, string> loCompilerOptions = new Dictionary<string, string>();
310 loCompilerOptions.Add("CompilerVersion", "v3.5");
311 CSharpCodeProvider loCompiler = new CSharpCodeProvider(loCompilerOptions);
312 CompilerParameters loParameters = new CompilerParameters();
314 // *** Start by adding any referenced assemblies
315 loParameters.ReferencedAssemblies.Add("OpenMetaverse.StructuredData.dll");
316 loParameters.ReferencedAssemblies.Add("OpenMetaverseTypes.dll");
317 loParameters.ReferencedAssemblies.Add("OpenMetaverse.dll");
318 loParameters.ReferencedAssemblies.Add("Radegast.exe");
319 loParameters.ReferencedAssemblies.Add("System.dll");
320 loParameters.ReferencedAssemblies.Add("System.Core.dll");
321 loParameters.ReferencedAssemblies.Add("System.Xml.dll");
322 loParameters.ReferencedAssemblies.Add("System.Drawing.dll");
323 loParameters.ReferencedAssemblies.Add("System.Windows.Forms.dll");
325 // *** Load the resulting assembly into memory
326 loParameters.GenerateInMemory = true;
327 loParameters.GenerateExecutable = false;
329 // *** Now compile the whole thing
330 CompilerResults loCompiled =
331 loCompiler.CompileAssemblyFromSource(loParameters, code);
333 // *** Check for compilation erros
334 if (loCompiled.Errors.HasErrors)
336 string lcErrorMsg = "";
337 lcErrorMsg = "Compilation failed: " + loCompiled.Errors.Count.ToString() + " errors:";
339 for (int x = 0; x < loCompiled.Errors.Count; x++)
340 lcErrorMsg += "\r\nLine: " +
341 loCompiled.Errors[x].Line.ToString() + " - " +
342 loCompiled.Errors[x].ErrorText;
344 instance.TabConsole.DisplayNotificationInChat(lcErrorMsg, ChatBufferTextStyle.Alert);
348 instance.TabConsole.DisplayNotificationInChat("Compilation successful.");
349 Assembly loAssembly = loCompiled.CompiledAssembly;
350 LoadAssembly(fileName, loAssembly, startPlugins);
354 Logger.Log("Failed loading C# script: ", Helpers.LogLevel.Warning, ex);
359 /// Scans assembly for supported types
361 /// <param name="loadfilename">File name from which assembly was loaded</param>
362 /// <param name="assembly">Assembly to scan for supported types</param>
363 /// <param name="startPlugins">Start plugins found in the assembly after complilation</param>
364 public void LoadAssembly(string loadfilename, Assembly assembly, bool startPlugins)
366 if (null != PluginsLoaded.Find((PluginInfo info) => { return info.FileName == loadfilename; }))
368 Logger.Log("Plugin already loaded, skipping: " + loadfilename, Helpers.LogLevel.Info);
371 instance.TabConsole.DisplayNotificationInChat("Plugin already loaded, skipping: " + loadfilename);
376 foreach (Type type in assembly.GetTypes())
378 if (typeof(IRadegastPlugin).IsAssignableFrom(type))
380 if (type.IsInterface) continue;
383 IRadegastPlugin plug = null;
384 ConstructorInfo constructorInfo = type.GetConstructor(new Type[] { typeof(RadegastInstance) });
385 if (constructorInfo != null)
386 plug = (IRadegastPlugin)constructorInfo.Invoke(new[] { instance });
389 constructorInfo = type.GetConstructor(new Type[] { });
390 if (constructorInfo != null)
391 plug = (IRadegastPlugin)constructorInfo.Invoke(new object[0]);
394 Logger.Log("ERROR Constructing Radegast Plugin: " + loadfilename + " because " + type + " has no usable constructor.", Helpers.LogLevel.Debug);
398 PluginInfo info = new PluginInfo()
400 FileName = loadfilename,
405 lock (PluginsLoaded) PluginsLoaded.Add(info);
406 if (startPlugins && plug != null)
408 try { plug.StartPlugin(instance); info.Started = true; }
409 catch (Exception ex) { Logger.Log(string.Format("Failed starting plugin {0}:", type), Helpers.LogLevel.Error, ex); }
414 Logger.Log("ERROR Constructing Radegast Plugin: " + loadfilename + " because " + ex,
415 Helpers.LogLevel.Debug);
422 instance.CommandsManager.LoadType(type);
423 instance.ContextActionManager.LoadType(type);
427 Logger.Log("ERROR in Radegast Plugin: " + loadfilename + " Command: " + type +
428 " because " + ex.Message + " " + ex.StackTrace, Helpers.LogLevel.Debug);