diff options
Diffstat (limited to 'OpenSim/Framework/Serialization/External/ExternalRepresentationUtils.cs')
-rw-r--r-- | OpenSim/Framework/Serialization/External/ExternalRepresentationUtils.cs | 411 |
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 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.Diagnostics; | ||
31 | using System.IO; | ||
32 | using System.Reflection; | ||
33 | using System.Xml; | ||
34 | using log4net; | ||
35 | using OpenMetaverse; | ||
36 | using OpenSim.Services.Interfaces; | ||
37 | |||
38 | namespace 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 | } | ||