aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Framework/Serialization/External/ExternalRepresentationUtils.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Framework/Serialization/External/ExternalRepresentationUtils.cs')
-rw-r--r--OpenSim/Framework/Serialization/External/ExternalRepresentationUtils.cs411
1 files changed, 411 insertions, 0 deletions
diff --git a/OpenSim/Framework/Serialization/External/ExternalRepresentationUtils.cs b/OpenSim/Framework/Serialization/External/ExternalRepresentationUtils.cs
new file mode 100644
index 0000000..55640ac
--- /dev/null
+++ b/OpenSim/Framework/Serialization/External/ExternalRepresentationUtils.cs
@@ -0,0 +1,411 @@
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.Collections.Generic;
30using System.Diagnostics;
31using System.IO;
32using System.Reflection;
33using System.Xml;
34using log4net;
35using OpenMetaverse;
36using OpenSim.Services.Interfaces;
37
38namespace OpenSim.Framework.Serialization.External
39{
40 /// <summary>
41 /// Utilities for manipulating external representations of data structures in OpenSim
42 /// </summary>
43 public class ExternalRepresentationUtils
44 {
45 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
46
47 /// <summary>
48 /// Populate a node with data read from xml using a dictinoary of processors
49 /// </summary>
50 /// <param name="nodeToFill"></param>
51 /// <param name="processors"></param>
52 /// <param name="xtr"></param>
53 /// <returns>true on successful, false if there were any processing failures</returns>
54 public static bool ExecuteReadProcessors<NodeType>(
55 NodeType nodeToFill, Dictionary<string, Action<NodeType, XmlReader>> processors, XmlReader xtr)
56 {
57 return ExecuteReadProcessors(
58 nodeToFill,
59 processors,
60 xtr,
61 (o, nodeName, e) => {
62 m_log.Debug(string.Format("[ExternalRepresentationUtils]: Error while parsing element {0} ",
63 nodeName), e);
64 });
65 }
66
67 /// <summary>
68 /// Populate a node with data read from xml using a dictinoary of processors
69 /// </summary>
70 /// <param name="nodeToFill"></param>
71 /// <param name="processors"></param>
72 /// <param name="xtr"></param>
73 /// <param name="parseExceptionAction">
74 /// Action to take if there is a parsing problem. This will usually just be to log the exception
75 /// </param>
76 /// <returns>true on successful, false if there were any processing failures</returns>
77 public static bool ExecuteReadProcessors<NodeType>(
78 NodeType nodeToFill,
79 Dictionary<string, Action<NodeType, XmlReader>> processors,
80 XmlReader xtr,
81 Action<NodeType, string, Exception> parseExceptionAction)
82 {
83 bool errors = false;
84 int numErrors = 0;
85
86 Stopwatch timer = new Stopwatch();
87 timer.Start();
88
89 string nodeName = string.Empty;
90 while (xtr.NodeType != XmlNodeType.EndElement)
91 {
92 nodeName = xtr.Name;
93
94 // m_log.DebugFormat("[ExternalRepresentationUtils]: Processing node: {0}", nodeName);
95
96 Action<NodeType, XmlReader> p = null;
97 if (processors.TryGetValue(xtr.Name, out p))
98 {
99 // m_log.DebugFormat("[ExternalRepresentationUtils]: Found processor for {0}", nodeName);
100
101 try
102 {
103 p(nodeToFill, xtr);
104 }
105 catch (Exception e)
106 {
107 errors = true;
108 parseExceptionAction(nodeToFill, nodeName, e);
109
110 if (xtr.EOF)
111 {
112 m_log.Debug("[ExternalRepresentationUtils]: Aborting ExecuteReadProcessors due to unexpected end of XML");
113 break;
114 }
115
116 if (++numErrors == 10)
117 {
118 m_log.Debug("[ExternalRepresentationUtils]: Aborting ExecuteReadProcessors due to too many parsing errors");
119 break;
120 }
121
122 if (xtr.NodeType == XmlNodeType.EndElement)
123 xtr.Read();
124 }
125 }
126 else
127 {
128 // m_log.DebugFormat("[ExternalRepresentationUtils]: found unknown element \"{0}\"", nodeName);
129 xtr.ReadOuterXml(); // ignore
130 }
131
132 if (timer.Elapsed.TotalSeconds >= 60)
133 {
134 m_log.Debug("[ExternalRepresentationUtils]: Aborting ExecuteReadProcessors due to timeout");
135 errors = true;
136 break;
137 }
138 }
139
140 return errors;
141 }
142
143 /// <summary>
144 /// Takes a XML representation of a SceneObjectPart and returns another XML representation
145 /// with creator data added to it.
146 /// </summary>
147 /// <param name="xml">The SceneObjectPart represented in XML2</param>
148 /// <param name="homeURL">The URL of the user agents service (home) for the creator</param>
149 /// <param name="userService">The service for retrieving user account information</param>
150 /// <param name="scopeID">The scope of the user account information (Grid ID)</param>
151 /// <returns>The SceneObjectPart represented in XML2</returns>
152 [Obsolete("This method is deprecated. Use RewriteSOP instead.")]
153 public static string RewriteSOP_Old(string xml, string homeURL, IUserAccountService userService, UUID scopeID)
154 {
155 if (xml == string.Empty || homeURL == string.Empty || userService == null)
156 return xml;
157
158 XmlDocument doc = new XmlDocument();
159 doc.LoadXml(xml);
160 XmlNodeList sops = doc.GetElementsByTagName("SceneObjectPart");
161
162 foreach (XmlNode sop in sops)
163 {
164 UserAccount creator = null;
165 bool hasCreatorData = false;
166 XmlNodeList nodes = sop.ChildNodes;
167 foreach (XmlNode node in nodes)
168 {
169 if (node.Name == "CreatorID")
170 {
171 UUID uuid = UUID.Zero;
172 UUID.TryParse(node.InnerText, out uuid);
173 creator = userService.GetUserAccount(scopeID, uuid);
174 }
175
176 if (node.Name == "CreatorData" && node.InnerText != null && node.InnerText != string.Empty)
177 hasCreatorData = true;
178
179 //if (node.Name == "OwnerID")
180 //{
181 // UserAccount owner = GetUser(node.InnerText);
182 // if (owner != null)
183 // node.InnerText = m_ProfileServiceURL + "/" + node.InnerText + "/" + owner.FirstName + " " + owner.LastName;
184 //}
185 }
186 if (!hasCreatorData && creator != null)
187 {
188 XmlElement creatorData = doc.CreateElement("CreatorData");
189 creatorData.InnerText = CalcCreatorData(homeURL, creator.FirstName + " " + creator.LastName);
190 sop.AppendChild(creatorData);
191 }
192 }
193
194 using (StringWriter wr = new StringWriter())
195 {
196 doc.Save(wr);
197 return wr.ToString();
198 }
199 }
200
201 /// <summary>
202 /// Takes a XML representation of a SceneObjectPart and returns another XML representation
203 /// with creator data added to it.
204 /// </summary>
205 /// <param name="xml">The SceneObjectPart represented in XML2</param>
206 /// <param name="sceneName">An identifier for the component that's calling this function</param>
207 /// <param name="homeURL">The URL of the user agents service (home) for the creator</param>
208 /// <param name="userService">The service for retrieving user account information</param>
209 /// <param name="scopeID">The scope of the user account information (Grid ID)</param>
210 /// <returns>The SceneObjectPart represented in XML2</returns>
211 public static string RewriteSOP(string xmlData, string sceneName, string homeURL, IUserAccountService userService, UUID scopeID)
212 {
213 // Console.WriteLine("Input XML [{0}]", xmlData);
214 if (xmlData == string.Empty || homeURL == string.Empty || userService == null)
215 return xmlData;
216
217 // Deal with bug
218 xmlData = ExternalRepresentationUtils.SanitizeXml(xmlData);
219
220 using (StringWriter sw = new StringWriter())
221 using (XmlTextWriter writer = new XmlTextWriter(sw))
222 using (XmlTextReader wrappedReader = new XmlTextReader(xmlData, XmlNodeType.Element, null))
223 using (XmlReader reader = XmlReader.Create(wrappedReader, new XmlReaderSettings() { IgnoreWhitespace = true, ConformanceLevel = ConformanceLevel.Fragment }))
224 {
225 TransformXml(reader, writer, sceneName, homeURL, userService, scopeID);
226
227 // Console.WriteLine("Output: [{0}]", sw.ToString());
228
229 return sw.ToString();
230 }
231 }
232
233 protected static void TransformXml(XmlReader reader, XmlWriter writer, string sceneName, string homeURI, IUserAccountService userAccountService, UUID scopeID)
234 {
235 // m_log.DebugFormat("[HG ASSET MAPPER]: Transforming XML");
236
237 int sopDepth = -1;
238 UserAccount creator = null;
239 bool hasCreatorData = false;
240
241 while (reader.Read())
242 {
243 // Console.WriteLine("Depth: {0}, name {1}", reader.Depth, reader.Name);
244
245 switch (reader.NodeType)
246 {
247 case XmlNodeType.Attribute:
248 // Console.WriteLine("FOUND ATTRIBUTE {0}", reader.Name);
249 writer.WriteAttributeString(reader.Name, reader.Value);
250 break;
251
252 case XmlNodeType.CDATA:
253 writer.WriteCData(reader.Value);
254 break;
255
256 case XmlNodeType.Comment:
257 writer.WriteComment(reader.Value);
258 break;
259
260 case XmlNodeType.DocumentType:
261 writer.WriteDocType(reader.Name, reader.Value, null, null);
262 break;
263
264 case XmlNodeType.Element:
265 // m_log.DebugFormat("Depth {0} at element {1}", reader.Depth, reader.Name);
266
267 writer.WriteStartElement(reader.Prefix, reader.LocalName, reader.NamespaceURI);
268
269 if (reader.HasAttributes)
270 {
271 while (reader.MoveToNextAttribute())
272 writer.WriteAttributeString(reader.Name, reader.Value);
273
274 reader.MoveToElement();
275 }
276
277 if (reader.LocalName == "SceneObjectPart")
278 {
279 if (sopDepth < 0)
280 {
281 sopDepth = reader.Depth;
282 // m_log.DebugFormat("[HG ASSET MAPPER]: Set sopDepth to {0}", sopDepth);
283 }
284 }
285 else
286 {
287 if (sopDepth >= 0 && reader.Depth == sopDepth + 1)
288 {
289 if (reader.Name == "CreatorID")
290 {
291 reader.Read();
292 if (reader.NodeType == XmlNodeType.Element && reader.Name == "Guid" || reader.Name == "UUID")
293 {
294 reader.Read();
295
296 if (reader.NodeType == XmlNodeType.Text)
297 {
298 UUID uuid = UUID.Zero;
299 UUID.TryParse(reader.Value, out uuid);
300 creator = userAccountService.GetUserAccount(scopeID, uuid);
301 writer.WriteElementString("UUID", reader.Value);
302 reader.Read();
303 }
304 else
305 {
306 // If we unexpected run across mixed content in this node, still carry on
307 // transforming the subtree (this replicates earlier behaviour).
308 TransformXml(reader, writer, sceneName, homeURI, userAccountService, scopeID);
309 }
310 }
311 else
312 {
313 // If we unexpected run across mixed content in this node, still carry on
314 // transforming the subtree (this replicates earlier behaviour).
315 TransformXml(reader, writer, sceneName, homeURI, userAccountService, scopeID);
316 }
317 }
318 else if (reader.Name == "CreatorData")
319 {
320 reader.Read();
321 if (reader.NodeType == XmlNodeType.Text)
322 {
323 hasCreatorData = true;
324 writer.WriteString(reader.Value);
325 }
326 else
327 {
328 // If we unexpected run across mixed content in this node, still carry on
329 // transforming the subtree (this replicates earlier behaviour).
330 TransformXml(reader, writer, sceneName, homeURI, userAccountService, scopeID);
331 }
332 }
333 }
334 }
335
336 if (reader.IsEmptyElement)
337 {
338 // m_log.DebugFormat("[HG ASSET MAPPER]: Writing end for empty element {0}", reader.Name);
339 writer.WriteEndElement();
340 }
341
342 break;
343
344 case XmlNodeType.EndElement:
345 // m_log.DebugFormat("Depth {0} at EndElement", reader.Depth);
346 if (sopDepth == reader.Depth)
347 {
348 if (!hasCreatorData && creator != null)
349 writer.WriteElementString(reader.Prefix, "CreatorData", reader.NamespaceURI, string.Format("{0};{1} {2}", homeURI, creator.FirstName, creator.LastName));
350
351 // m_log.DebugFormat("[HG ASSET MAPPER]: Reset sopDepth");
352 sopDepth = -1;
353 creator = null;
354 hasCreatorData = false;
355 }
356 writer.WriteEndElement();
357 break;
358
359 case XmlNodeType.EntityReference:
360 writer.WriteEntityRef(reader.Name);
361 break;
362
363 case XmlNodeType.ProcessingInstruction:
364 writer.WriteProcessingInstruction(reader.Name, reader.Value);
365 break;
366
367 case XmlNodeType.Text:
368 writer.WriteString(reader.Value);
369 break;
370
371 case XmlNodeType.XmlDeclaration:
372 // For various reasons, not all serializations have xml declarations (or consistent ones)
373 // and as it's embedded inside a byte stream we don't need it anyway, so ignore.
374 break;
375
376 default:
377 m_log.WarnFormat(
378 "[HG ASSET MAPPER]: Unrecognized node {0} in asset XML transform in {1}",
379 reader.NodeType, sceneName);
380 break;
381 }
382 }
383 }
384
385 public static string CalcCreatorData(string homeURL, string name)
386 {
387 return homeURL + ";" + name;
388 }
389
390 internal static string CalcCreatorData(string homeURL, UUID uuid, string name)
391 {
392 return homeURL + "/" + uuid + ";" + name;
393 }
394
395 /// <summary>
396 /// Sanitation for bug introduced in Oct. 20 (1eb3e6cc43e2a7b4053bc1185c7c88e22356c5e8)
397 /// </summary>
398 /// <param name="xmlData"></param>
399 /// <returns></returns>
400 public static string SanitizeXml(string xmlData)
401 {
402 string fixedData = xmlData;
403 if (fixedData != null)
404 // Loop, because it may contain multiple
405 while (fixedData.Contains("xmlns:xmlns:"))
406 fixedData = fixedData.Replace("xmlns:xmlns:", "xmlns:");
407 return fixedData;
408 }
409
410 }
411}