diff options
Diffstat (limited to 'OpenSim/Server/Base/ServerUtils.cs')
-rw-r--r-- | OpenSim/Server/Base/ServerUtils.cs | 542 |
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 | |||
28 | using System; | ||
29 | using System.IO; | ||
30 | using System.Reflection; | ||
31 | using System.Xml; | ||
32 | using System.Xml.Serialization; | ||
33 | using System.Text; | ||
34 | using System.Collections.Generic; | ||
35 | using log4net; | ||
36 | using Nini.Config; | ||
37 | using OpenSim.Framework; | ||
38 | using OpenMetaverse; | ||
39 | using Mono.Addins; | ||
40 | using OpenSim.Framework.Servers.HttpServer; | ||
41 | using OpenSim.Framework.Servers; | ||
42 | |||
43 | |||
44 | [assembly:AddinRoot("Robust", OpenSim.VersionInfo.VersionNumber)] | ||
45 | namespace 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 | } | ||