aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Server/Base/ServerUtils.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Server/Base/ServerUtils.cs')
-rw-r--r--OpenSim/Server/Base/ServerUtils.cs542
1 files changed, 542 insertions, 0 deletions
diff --git a/OpenSim/Server/Base/ServerUtils.cs b/OpenSim/Server/Base/ServerUtils.cs
new file mode 100644
index 0000000..18a4266
--- /dev/null
+++ b/OpenSim/Server/Base/ServerUtils.cs
@@ -0,0 +1,542 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.IO;
30using System.Reflection;
31using System.Xml;
32using System.Xml.Serialization;
33using System.Text;
34using System.Collections.Generic;
35using log4net;
36using Nini.Config;
37using OpenSim.Framework;
38using OpenMetaverse;
39using Mono.Addins;
40using OpenSim.Framework.Servers.HttpServer;
41using OpenSim.Framework.Servers;
42
43
44[assembly:AddinRoot("Robust", OpenSim.VersionInfo.VersionNumber)]
45namespace OpenSim.Server.Base
46{
47 [TypeExtensionPoint(Path="/Robust/Connector", Name="RobustConnector")]
48 public interface IRobustConnector
49 {
50 string ConfigName
51 {
52 get;
53 }
54
55 bool Enabled
56 {
57 get;
58 }
59
60 string PluginPath
61 {
62 get;
63 set;
64 }
65
66 uint Configure(IConfigSource config);
67 void Initialize(IHttpServer server);
68 void Unload();
69 }
70
71 public class PluginLoader
72 {
73 static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
74
75 public AddinRegistry Registry
76 {
77 get;
78 private set;
79 }
80
81 public IConfigSource Config
82 {
83 get;
84 private set;
85 }
86
87 public PluginLoader(IConfigSource config, string registryPath)
88 {
89 Config = config;
90
91 Registry = new AddinRegistry(registryPath, ".");
92 //suppress_console_output_(true);
93 AddinManager.Initialize(registryPath);
94 //suppress_console_output_(false);
95 AddinManager.Registry.Update();
96 CommandManager commandmanager = new CommandManager(Registry);
97 AddinManager.AddExtensionNodeHandler("/Robust/Connector", OnExtensionChanged);
98 }
99
100 private static TextWriter prev_console_;
101 // Temporarily masking the errors reported on start
102 // This is caused by a non-managed dll in the ./bin dir
103 // when the registry is initialized. The dll belongs to
104 // libomv, which has a hard-coded path to "." for pinvoke
105 // to load the openjpeg dll
106 //
107 // Will look for a way to fix, but for now this keeps the
108 // confusion to a minimum. this was copied from our region
109 // plugin loader, we have been doing this in there for a long time.
110 //
111 public void suppress_console_output_(bool save)
112 {
113 if (save)
114 {
115 prev_console_ = System.Console.Out;
116 System.Console.SetOut(new StreamWriter(Stream.Null));
117 }
118 else
119 {
120 if (prev_console_ != null)
121 System.Console.SetOut(prev_console_);
122 }
123 }
124
125 private void OnExtensionChanged(object s, ExtensionNodeEventArgs args)
126 {
127 IRobustConnector connector = (IRobustConnector)args.ExtensionObject;
128 Addin a = Registry.GetAddin(args.ExtensionNode.Addin.Id);
129
130 if(a == null)
131 {
132 Registry.Rebuild(null);
133 a = Registry.GetAddin(args.ExtensionNode.Addin.Id);
134 }
135
136 switch(args.Change)
137 {
138 case ExtensionChange.Add:
139 if (a.AddinFile.Contains(Registry.DefaultAddinsFolder))
140 {
141 m_log.InfoFormat("[SERVER UTILS]: Adding {0} from registry", a.Name);
142 connector.PluginPath = System.IO.Path.Combine(Registry.DefaultAddinsFolder,a.Name.Replace(',', '.')); }
143 else
144 {
145 m_log.InfoFormat("[SERVER UTILS]: Adding {0} from ./bin", a.Name);
146 connector.PluginPath = a.AddinFile;
147 }
148 LoadPlugin(connector);
149 break;
150 case ExtensionChange.Remove:
151 m_log.InfoFormat("[SERVER UTILS]: Removing {0}", a.Name);
152 UnloadPlugin(connector);
153 break;
154 }
155 }
156
157 private void LoadPlugin(IRobustConnector connector)
158 {
159 IHttpServer server = null;
160 uint port = connector.Configure(Config);
161
162 if(connector.Enabled)
163 {
164 server = GetServer(connector, port);
165 connector.Initialize(server);
166 }
167 else
168 {
169 m_log.InfoFormat("[SERVER UTILS]: {0} Disabled.", connector.ConfigName);
170 }
171 }
172
173 private void UnloadPlugin(IRobustConnector connector)
174 {
175 m_log.InfoFormat("[SERVER UTILS]: Unloading {0}", connector.ConfigName);
176
177 connector.Unload();
178 }
179
180 private IHttpServer GetServer(IRobustConnector connector, uint port)
181 {
182 IHttpServer server;
183
184 if(port != 0)
185 server = MainServer.GetHttpServer(port);
186 else
187 server = MainServer.Instance;
188
189 return server;
190 }
191 }
192
193 public static class ServerUtils
194 {
195 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
196
197 public static byte[] SerializeResult(XmlSerializer xs, object data)
198 {
199 using (MemoryStream ms = new MemoryStream())
200 using (XmlTextWriter xw = new XmlTextWriter(ms, Util.UTF8))
201 {
202 xw.Formatting = Formatting.Indented;
203 xs.Serialize(xw, data);
204 xw.Flush();
205
206 ms.Seek(0, SeekOrigin.Begin);
207 byte[] ret = ms.GetBuffer();
208 Array.Resize(ref ret, (int)ms.Length);
209
210 return ret;
211 }
212 }
213
214 /// <summary>
215 /// Load a plugin from a dll with the given class or interface
216 /// </summary>
217 /// <param name="dllName"></param>
218 /// <param name="args">The arguments which control which constructor is invoked on the plugin</param>
219 /// <returns></returns>
220 public static T LoadPlugin<T> (string dllName, Object[] args) where T:class
221 {
222 // This is good to debug configuration problems
223 //if (dllName == string.Empty)
224 // Util.PrintCallStack();
225
226 string className = String.Empty;
227
228 // The path for a dynamic plugin will contain ":" on Windows
229 string[] parts = dllName.Split (new char[] {':'});
230
231 if (parts [0].Length > 1)
232 {
233 dllName = parts [0];
234 if (parts.Length > 1)
235 className = parts[1];
236 }
237 else
238 {
239 // This is Windows - we must replace the ":" in the path
240 dllName = String.Format ("{0}:{1}", parts [0], parts [1]);
241 if (parts.Length > 2)
242 className = parts[2];
243 }
244
245 return LoadPlugin<T>(dllName, className, args);
246 }
247
248 /// <summary>
249 /// Load a plugin from a dll with the given class or interface
250 /// </summary>
251 /// <param name="dllName"></param>
252 /// <param name="className"></param>
253 /// <param name="args">The arguments which control which constructor is invoked on the plugin</param>
254 /// <returns></returns>
255 public static T LoadPlugin<T>(string dllName, string className, Object[] args) where T:class
256 {
257 string interfaceName = typeof(T).ToString();
258
259 try
260 {
261 Assembly pluginAssembly = Assembly.LoadFrom(dllName);
262
263 foreach (Type pluginType in pluginAssembly.GetTypes())
264 {
265 if (pluginType.IsPublic)
266 {
267 if (className != String.Empty
268 && pluginType.ToString() != pluginType.Namespace + "." + className)
269 continue;
270
271 Type typeInterface = pluginType.GetInterface(interfaceName);
272
273 if (typeInterface != null)
274 {
275 T plug = null;
276 try
277 {
278 plug = (T)Activator.CreateInstance(pluginType,
279 args);
280 }
281 catch (Exception e)
282 {
283 if (!(e is System.MissingMethodException))
284 {
285 m_log.Error(string.Format("[SERVER UTILS]: Error loading plugin {0} from {1}. Exception: {2}",
286 interfaceName,
287 dllName,
288 e.InnerException == null ? e.Message : e.InnerException.Message),
289 e);
290 }
291 m_log.ErrorFormat("[SERVER UTILS]: Error loading plugin {0}: {1} args.Length {2}", dllName, e.Message, args.Length);
292 return null;
293 }
294
295 return plug;
296 }
297 }
298 }
299
300 return null;
301 }
302 catch (ReflectionTypeLoadException rtle)
303 {
304 m_log.Error(string.Format("[SERVER UTILS]: Error loading plugin from {0}:\n{1}", dllName,
305 String.Join("\n", Array.ConvertAll(rtle.LoaderExceptions, e => e.ToString()))),
306 rtle);
307 return null;
308 }
309 catch (Exception e)
310 {
311 m_log.Error(string.Format("[SERVER UTILS]: Error loading plugin from {0}", dllName), e);
312 return null;
313 }
314 }
315
316 public static Dictionary<string, object> ParseQueryString(string query)
317 {
318 Dictionary<string, object> result = new Dictionary<string, object>();
319 string[] terms = query.Split(new char[] {'&'});
320
321 if (terms.Length == 0)
322 return result;
323
324 foreach (string t in terms)
325 {
326 string[] elems = t.Split(new char[] {'='});
327 if (elems.Length == 0)
328 continue;
329
330 string name = System.Web.HttpUtility.UrlDecode(elems[0]);
331 string value = String.Empty;
332
333 if (elems.Length > 1)
334 value = System.Web.HttpUtility.UrlDecode(elems[1]);
335
336 if (name.EndsWith("[]"))
337 {
338 string cleanName = name.Substring(0, name.Length - 2);
339 if (result.ContainsKey(cleanName))
340 {
341 if (!(result[cleanName] is List<string>))
342 continue;
343
344 List<string> l = (List<string>)result[cleanName];
345
346 l.Add(value);
347 }
348 else
349 {
350 List<string> newList = new List<string>();
351
352 newList.Add(value);
353
354 result[cleanName] = newList;
355 }
356 }
357 else
358 {
359 if (!result.ContainsKey(name))
360 result[name] = value;
361 }
362 }
363
364 return result;
365 }
366
367 public static string BuildQueryString(Dictionary<string, object> data)
368 {
369 string qstring = String.Empty;
370
371 string part;
372
373 foreach (KeyValuePair<string, object> kvp in data)
374 {
375 if (kvp.Value is List<string>)
376 {
377 List<string> l = (List<String>)kvp.Value;
378
379 foreach (string s in l)
380 {
381 part = System.Web.HttpUtility.UrlEncode(kvp.Key) +
382 "[]=" + System.Web.HttpUtility.UrlEncode(s);
383
384 if (qstring != String.Empty)
385 qstring += "&";
386
387 qstring += part;
388 }
389 }
390 else
391 {
392 if (kvp.Value.ToString() != String.Empty)
393 {
394 part = System.Web.HttpUtility.UrlEncode(kvp.Key) +
395 "=" + System.Web.HttpUtility.UrlEncode(kvp.Value.ToString());
396 }
397 else
398 {
399 part = System.Web.HttpUtility.UrlEncode(kvp.Key);
400 }
401
402 if (qstring != String.Empty)
403 qstring += "&";
404
405 qstring += part;
406 }
407 }
408
409 return qstring;
410 }
411
412 public static string BuildXmlResponse(Dictionary<string, object> data)
413 {
414 XmlDocument doc = new XmlDocument();
415
416 XmlNode xmlnode = doc.CreateNode(XmlNodeType.XmlDeclaration,
417 "", "");
418
419 doc.AppendChild(xmlnode);
420
421 XmlElement rootElement = doc.CreateElement("", "ServerResponse",
422 "");
423
424 doc.AppendChild(rootElement);
425
426 BuildXmlData(rootElement, data);
427
428 return doc.InnerXml;
429 }
430
431 private static void BuildXmlData(XmlElement parent, Dictionary<string, object> data)
432 {
433 foreach (KeyValuePair<string, object> kvp in data)
434 {
435 if (kvp.Value == null)
436 continue;
437
438 XmlElement elem = parent.OwnerDocument.CreateElement("",
439 XmlConvert.EncodeLocalName(kvp.Key), "");
440
441 if (kvp.Value is Dictionary<string, object>)
442 {
443 XmlAttribute type = parent.OwnerDocument.CreateAttribute("",
444 "type", "");
445 type.Value = "List";
446
447 elem.Attributes.Append(type);
448
449 BuildXmlData(elem, (Dictionary<string, object>)kvp.Value);
450 }
451 else
452 {
453 elem.AppendChild(parent.OwnerDocument.CreateTextNode(
454 kvp.Value.ToString()));
455 }
456
457 parent.AppendChild(elem);
458 }
459 }
460
461 public static Dictionary<string, object> ParseXmlResponse(string data)
462 {
463 //m_log.DebugFormat("[XXX]: received xml string: {0}", data);
464
465 Dictionary<string, object> ret = new Dictionary<string, object>();
466
467 XmlDocument doc = new XmlDocument();
468
469 doc.LoadXml(data);
470
471 XmlNodeList rootL = doc.GetElementsByTagName("ServerResponse");
472
473 if (rootL.Count != 1)
474 return ret;
475
476 XmlNode rootNode = rootL[0];
477
478 ret = ParseElement(rootNode);
479
480 return ret;
481 }
482
483 private static Dictionary<string, object> ParseElement(XmlNode element)
484 {
485 Dictionary<string, object> ret = new Dictionary<string, object>();
486
487 XmlNodeList partL = element.ChildNodes;
488
489 foreach (XmlNode part in partL)
490 {
491 XmlNode type = part.Attributes.GetNamedItem("type");
492 if (type == null || type.Value != "List")
493 {
494 ret[XmlConvert.DecodeName(part.Name)] = part.InnerText;
495 }
496 else
497 {
498 ret[XmlConvert.DecodeName(part.Name)] = ParseElement(part);
499 }
500 }
501
502 return ret;
503 }
504
505 public static IConfig GetConfig(string configFile, string configName)
506 {
507 IConfig config;
508
509 if (File.Exists(configFile))
510 {
511 IConfigSource configsource = new IniConfigSource(configFile);
512 config = configsource.Configs[configName];
513 }
514 else
515 config = null;
516
517 return config;
518 }
519
520 public static IConfigSource LoadInitialConfig(string url)
521 {
522 IConfigSource source = new XmlConfigSource();
523 m_log.InfoFormat("[SERVER UTILS]: {0} is a http:// URI, fetching ...", url);
524
525 // The ini file path is a http URI
526 // Try to read it
527 try
528 {
529 XmlReader r = XmlReader.Create(url);
530 IConfigSource cs = new XmlConfigSource(r);
531 source.Merge(cs);
532 }
533 catch (Exception e)
534 {
535 m_log.FatalFormat("[SERVER UTILS]: Exception reading config from URI {0}\n" + e.ToString(), url);
536 Environment.Exit(1);
537 }
538
539 return source;
540 }
541 }
542}