corrade-vassal – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 vero 1 /*
2 * Copyright (c) 2006-2014, openmetaverse.org
3 * All rights reserved.
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 *
8 * - Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 * - Neither the name of the openmetaverse.org nor the names
11 * of its contributors may be used to endorse or promote products derived from
12 * this software without specific prior written permission.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
18 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 */
26  
27 using System;
28 using System.Collections.Generic;
29 using System.IO;
30 using System.Xml;
31 using System.Xml.Schema;
32 using System.Text;
33  
34 namespace OpenMetaverse.StructuredData
35 {
36 /// <summary>
37 ///
38 /// </summary>
39 public static partial class OSDParser
40 {
41 private static XmlSchema XmlSchema;
42 private static XmlTextReader XmlTextReader;
43 private static string LastXmlErrors = String.Empty;
44 private static object XmlValidationLock = new object();
45  
46 /// <summary>
47 ///
48 /// </summary>
49 /// <param name="xmlData"></param>
50 /// <returns></returns>
51 public static OSD DeserializeLLSDXml(byte[] xmlData)
52 {
53 return DeserializeLLSDXml(new XmlTextReader(new MemoryStream(xmlData, false)));
54 }
55  
56 public static OSD DeserializeLLSDXml(Stream xmlStream)
57 {
58 return DeserializeLLSDXml(new XmlTextReader(xmlStream));
59 }
60  
61 /// <summary>
62 ///
63 /// </summary>
64 /// <param name="xmlData"></param>
65 /// <returns></returns>
66 public static OSD DeserializeLLSDXml(string xmlData)
67 {
68 byte[] bytes = Utils.StringToBytes(xmlData);
69 return DeserializeLLSDXml(new XmlTextReader(new MemoryStream(bytes, false)));
70 }
71  
72 /// <summary>
73 ///
74 /// </summary>
75 /// <param name="xmlData"></param>
76 /// <returns></returns>
77 public static OSD DeserializeLLSDXml(XmlTextReader xmlData)
78 {
79 try
80 {
81 xmlData.Read();
82 SkipWhitespace(xmlData);
83  
84 xmlData.Read();
85 OSD ret = ParseLLSDXmlElement(xmlData);
86  
87 return ret;
88 }
89 catch
90 {
91 return new OSD();
92 }
93 }
94  
95 /// <summary>
96 ///
97 /// </summary>
98 /// <param name="data"></param>
99 /// <returns></returns>
100 public static byte[] SerializeLLSDXmlBytes(OSD data)
101 {
102 return Encoding.UTF8.GetBytes(SerializeLLSDXmlString(data));
103 }
104  
105 /// <summary>
106 ///
107 /// </summary>
108 /// <param name="data"></param>
109 /// <returns></returns>
110 public static string SerializeLLSDXmlString(OSD data)
111 {
112 StringWriter sw = new StringWriter();
113 XmlTextWriter writer = new XmlTextWriter(sw);
114 writer.Formatting = Formatting.None;
115  
116 writer.WriteStartElement(String.Empty, "llsd", String.Empty);
117 SerializeLLSDXmlElement(writer, data);
118 writer.WriteEndElement();
119  
120 writer.Close();
121  
122 return sw.ToString();
123 }
124  
125 /// <summary>
126 ///
127 /// </summary>
128 /// <param name="writer"></param>
129 /// <param name="data"></param>
130 public static void SerializeLLSDXmlElement(XmlTextWriter writer, OSD data)
131 {
132 switch (data.Type)
133 {
134 case OSDType.Unknown:
135 writer.WriteStartElement(String.Empty, "undef", String.Empty);
136 writer.WriteEndElement();
137 break;
138 case OSDType.Boolean:
139 writer.WriteStartElement(String.Empty, "boolean", String.Empty);
140 writer.WriteString(data.AsString());
141 writer.WriteEndElement();
142 break;
143 case OSDType.Integer:
144 writer.WriteStartElement(String.Empty, "integer", String.Empty);
145 writer.WriteString(data.AsString());
146 writer.WriteEndElement();
147 break;
148 case OSDType.Real:
149 writer.WriteStartElement(String.Empty, "real", String.Empty);
150 writer.WriteString(data.AsString());
151 writer.WriteEndElement();
152 break;
153 case OSDType.String:
154 writer.WriteStartElement(String.Empty, "string", String.Empty);
155 writer.WriteString(data.AsString());
156 writer.WriteEndElement();
157 break;
158 case OSDType.UUID:
159 writer.WriteStartElement(String.Empty, "uuid", String.Empty);
160 writer.WriteString(data.AsString());
161 writer.WriteEndElement();
162 break;
163 case OSDType.Date:
164 writer.WriteStartElement(String.Empty, "date", String.Empty);
165 writer.WriteString(data.AsString());
166 writer.WriteEndElement();
167 break;
168 case OSDType.URI:
169 writer.WriteStartElement(String.Empty, "uri", String.Empty);
170 writer.WriteString(data.AsString());
171 writer.WriteEndElement();
172 break;
173 case OSDType.Binary:
174 writer.WriteStartElement(String.Empty, "binary", String.Empty);
175 writer.WriteStartAttribute(String.Empty, "encoding", String.Empty);
176 writer.WriteString("base64");
177 writer.WriteEndAttribute();
178 writer.WriteString(data.AsString());
179 writer.WriteEndElement();
180 break;
181 case OSDType.Map:
182 OSDMap map = (OSDMap)data;
183 writer.WriteStartElement(String.Empty, "map", String.Empty);
184 foreach (KeyValuePair<string, OSD> kvp in map)
185 {
186 writer.WriteStartElement(String.Empty, "key", String.Empty);
187 writer.WriteString(kvp.Key);
188 writer.WriteEndElement();
189  
190 SerializeLLSDXmlElement(writer, kvp.Value);
191 }
192 writer.WriteEndElement();
193 break;
194 case OSDType.Array:
195 OSDArray array = (OSDArray)data;
196 writer.WriteStartElement(String.Empty, "array", String.Empty);
197 for (int i = 0; i < array.Count; i++)
198 {
199 SerializeLLSDXmlElement(writer, array[i]);
200 }
201 writer.WriteEndElement();
202 break;
203 }
204 }
205  
206 /// <summary>
207 ///
208 /// </summary>
209 /// <param name="xmlData"></param>
210 /// <param name="error"></param>
211 /// <returns></returns>
212 public static bool TryValidateLLSDXml(XmlTextReader xmlData, out string error)
213 {
214 lock (XmlValidationLock)
215 {
216 LastXmlErrors = String.Empty;
217 XmlTextReader = xmlData;
218  
219 CreateLLSDXmlSchema();
220  
221 XmlReaderSettings readerSettings = new XmlReaderSettings();
222 readerSettings.ValidationType = ValidationType.Schema;
223 readerSettings.Schemas.Add(XmlSchema);
224 readerSettings.ValidationEventHandler += new ValidationEventHandler(LLSDXmlSchemaValidationHandler);
225  
226 XmlReader reader = XmlReader.Create(xmlData, readerSettings);
227  
228 try
229 {
230 while (reader.Read()) { }
231 }
232 catch (XmlException)
233 {
234 error = LastXmlErrors;
235 return false;
236 }
237  
238 if (LastXmlErrors == String.Empty)
239 {
240 error = null;
241 return true;
242 }
243 else
244 {
245 error = LastXmlErrors;
246 return false;
247 }
248 }
249 }
250  
251 /// <summary>
252 ///
253 /// </summary>
254 /// <param name="reader"></param>
255 /// <returns></returns>
256 private static OSD ParseLLSDXmlElement(XmlTextReader reader)
257 {
258 SkipWhitespace(reader);
259  
260 if (reader.NodeType != XmlNodeType.Element)
261 throw new OSDException("Expected an element");
262  
263 string type = reader.LocalName;
264 OSD ret;
265  
266 switch (type)
267 {
268 case "undef":
269 if (reader.IsEmptyElement)
270 {
271 reader.Read();
272 return new OSD();
273 }
274  
275 reader.Read();
276 SkipWhitespace(reader);
277 ret = new OSD();
278 break;
279 case "boolean":
280 if (reader.IsEmptyElement)
281 {
282 reader.Read();
283 return OSD.FromBoolean(false);
284 }
285  
286 if (reader.Read())
287 {
288 string s = reader.ReadString().Trim();
289  
290 if (!String.IsNullOrEmpty(s) && (s == "true" || s == "1"))
291 {
292 ret = OSD.FromBoolean(true);
293 break;
294 }
295 }
296  
297 ret = OSD.FromBoolean(false);
298 break;
299 case "integer":
300 if (reader.IsEmptyElement)
301 {
302 reader.Read();
303 return OSD.FromInteger(0);
304 }
305  
306 if (reader.Read())
307 {
308 int value = 0;
309 Int32.TryParse(reader.ReadString().Trim(), out value);
310 ret = OSD.FromInteger(value);
311 break;
312 }
313  
314 ret = OSD.FromInteger(0);
315 break;
316 case "real":
317 if (reader.IsEmptyElement)
318 {
319 reader.Read();
320 return OSD.FromReal(0d);
321 }
322  
323 if (reader.Read())
324 {
325 double value = 0d;
326 string str = reader.ReadString().Trim().ToLower();
327  
328 if (str == "nan")
329 value = Double.NaN;
330 else
331 Utils.TryParseDouble(str, out value);
332  
333 ret = OSD.FromReal(value);
334 break;
335 }
336  
337 ret = OSD.FromReal(0d);
338 break;
339 case "uuid":
340 if (reader.IsEmptyElement)
341 {
342 reader.Read();
343 return OSD.FromUUID(UUID.Zero);
344 }
345  
346 if (reader.Read())
347 {
348 UUID value = UUID.Zero;
349 UUID.TryParse(reader.ReadString().Trim(), out value);
350 ret = OSD.FromUUID(value);
351 break;
352 }
353  
354 ret = OSD.FromUUID(UUID.Zero);
355 break;
356 case "date":
357 if (reader.IsEmptyElement)
358 {
359 reader.Read();
360 return OSD.FromDate(Utils.Epoch);
361 }
362  
363 if (reader.Read())
364 {
365 DateTime value = Utils.Epoch;
366 DateTime.TryParse(reader.ReadString().Trim(), out value);
367 ret = OSD.FromDate(value);
368 break;
369 }
370  
371 ret = OSD.FromDate(Utils.Epoch);
372 break;
373 case "string":
374 if (reader.IsEmptyElement)
375 {
376 reader.Read();
377 return OSD.FromString(String.Empty);
378 }
379  
380 if (reader.Read())
381 {
382 ret = OSD.FromString(reader.ReadString());
383 break;
384 }
385  
386 ret = OSD.FromString(String.Empty);
387 break;
388 case "binary":
389 if (reader.IsEmptyElement)
390 {
391 reader.Read();
392 return OSD.FromBinary(Utils.EmptyBytes);
393 }
394  
395 if (reader.GetAttribute("encoding") != null && reader.GetAttribute("encoding") != "base64")
396 throw new OSDException("Unsupported binary encoding: " + reader.GetAttribute("encoding"));
397  
398 if (reader.Read())
399 {
400 try
401 {
402 ret = OSD.FromBinary(Convert.FromBase64String(reader.ReadString().Trim()));
403 break;
404 }
405 catch (FormatException ex)
406 {
407 throw new OSDException("Binary decoding exception: " + ex.Message);
408 }
409 }
410  
411 ret = OSD.FromBinary(Utils.EmptyBytes);
412 break;
413 case "uri":
414 if (reader.IsEmptyElement)
415 {
416 reader.Read();
417 return OSD.FromUri(new Uri(String.Empty, UriKind.RelativeOrAbsolute));
418 }
419  
420 if (reader.Read())
421 {
422 ret = OSD.FromUri(new Uri(reader.ReadString(), UriKind.RelativeOrAbsolute));
423 break;
424 }
425  
426 ret = OSD.FromUri(new Uri(String.Empty, UriKind.RelativeOrAbsolute));
427 break;
428 case "map":
429 return ParseLLSDXmlMap(reader);
430 case "array":
431 return ParseLLSDXmlArray(reader);
432 default:
433 reader.Read();
434 ret = null;
435 break;
436 }
437  
438 if (reader.NodeType != XmlNodeType.EndElement || reader.LocalName != type)
439 {
440 throw new OSDException("Expected </" + type + ">");
441 }
442 else
443 {
444 reader.Read();
445 return ret;
446 }
447 }
448  
449 private static OSDMap ParseLLSDXmlMap(XmlTextReader reader)
450 {
451 if (reader.NodeType != XmlNodeType.Element || reader.LocalName != "map")
452 throw new NotImplementedException("Expected <map>");
453  
454 OSDMap map = new OSDMap();
455  
456 if (reader.IsEmptyElement)
457 {
458 reader.Read();
459 return map;
460 }
461  
462 if (reader.Read())
463 {
464 while (true)
465 {
466 SkipWhitespace(reader);
467  
468 if (reader.NodeType == XmlNodeType.EndElement && reader.LocalName == "map")
469 {
470 reader.Read();
471 break;
472 }
473  
474 if (reader.NodeType != XmlNodeType.Element || reader.LocalName != "key")
475 throw new OSDException("Expected <key>");
476  
477 string key = reader.ReadString();
478  
479 if (reader.NodeType != XmlNodeType.EndElement || reader.LocalName != "key")
480 throw new OSDException("Expected </key>");
481  
482 if (reader.Read())
483 map[key] = ParseLLSDXmlElement(reader);
484 else
485 throw new OSDException("Failed to parse a value for key " + key);
486 }
487 }
488  
489 return map;
490 }
491  
492 private static OSDArray ParseLLSDXmlArray(XmlTextReader reader)
493 {
494 if (reader.NodeType != XmlNodeType.Element || reader.LocalName != "array")
495 throw new OSDException("Expected <array>");
496  
497 OSDArray array = new OSDArray();
498  
499 if (reader.IsEmptyElement)
500 {
501 reader.Read();
502 return array;
503 }
504  
505 if (reader.Read())
506 {
507 while (true)
508 {
509 SkipWhitespace(reader);
510  
511 if (reader.NodeType == XmlNodeType.EndElement && reader.LocalName == "array")
512 {
513 reader.Read();
514 break;
515 }
516  
517 array.Add(ParseLLSDXmlElement(reader));
518 }
519 }
520  
521 return array;
522 }
523  
524 private static void SkipWhitespace(XmlTextReader reader)
525 {
526 while (
527 reader.NodeType == XmlNodeType.Comment ||
528 reader.NodeType == XmlNodeType.Whitespace ||
529 reader.NodeType == XmlNodeType.SignificantWhitespace ||
530 reader.NodeType == XmlNodeType.XmlDeclaration)
531 {
532 reader.Read();
533 }
534 }
535  
536 private static void CreateLLSDXmlSchema()
537 {
538 if (XmlSchema == null)
539 {
540 #region XSD
541 string schemaText = @"
542 <?xml version=""1.0"" encoding=""utf-8""?>
543 <xs:schema elementFormDefault=""qualified"" xmlns:xs=""http://www.w3.org/2001/XMLSchema"">
544 <xs:import schemaLocation=""xml.xsd"" namespace=""http://www.w3.org/XML/1998/namespace"" />
545 <xs:element name=""uri"" type=""xs:string"" />
546 <xs:element name=""uuid"" type=""xs:string"" />
547 <xs:element name=""KEYDATA"">
548 <xs:complexType>
549 <xs:sequence>
550 <xs:element ref=""key"" />
551 <xs:element ref=""DATA"" />
552 </xs:sequence>
553 </xs:complexType>
554 </xs:element>
555 <xs:element name=""date"" type=""xs:string"" />
556 <xs:element name=""key"" type=""xs:string"" />
557 <xs:element name=""boolean"" type=""xs:string"" />
558 <xs:element name=""undef"">
559 <xs:complexType>
560 <xs:sequence>
561 <xs:element ref=""EMPTY"" />
562 </xs:sequence>
563 </xs:complexType>
564 </xs:element>
565 <xs:element name=""map"">
566 <xs:complexType>
567 <xs:sequence>
568 <xs:element minOccurs=""0"" maxOccurs=""unbounded"" ref=""KEYDATA"" />
569 </xs:sequence>
570 </xs:complexType>
571 </xs:element>
572 <xs:element name=""real"" type=""xs:string"" />
573 <xs:element name=""ATOMIC"">
574 <xs:complexType>
575 <xs:choice>
576 <xs:element ref=""undef"" />
577 <xs:element ref=""boolean"" />
578 <xs:element ref=""integer"" />
579 <xs:element ref=""real"" />
580 <xs:element ref=""uuid"" />
581 <xs:element ref=""string"" />
582 <xs:element ref=""date"" />
583 <xs:element ref=""uri"" />
584 <xs:element ref=""binary"" />
585 </xs:choice>
586 </xs:complexType>
587 </xs:element>
588 <xs:element name=""DATA"">
589 <xs:complexType>
590 <xs:choice>
591 <xs:element ref=""ATOMIC"" />
592 <xs:element ref=""map"" />
593 <xs:element ref=""array"" />
594 </xs:choice>
595 </xs:complexType>
596 </xs:element>
597 <xs:element name=""llsd"">
598 <xs:complexType>
599 <xs:sequence>
600 <xs:element ref=""DATA"" />
601 </xs:sequence>
602 </xs:complexType>
603 </xs:element>
604 <xs:element name=""binary"">
605 <xs:complexType>
606 <xs:simpleContent>
607 <xs:extension base=""xs:string"">
608 <xs:attribute default=""base64"" name=""encoding"" type=""xs:string"" />
609 </xs:extension>
610 </xs:simpleContent>
611 </xs:complexType>
612 </xs:element>
613 <xs:element name=""array"">
614 <xs:complexType>
615 <xs:sequence>
616 <xs:element minOccurs=""0"" maxOccurs=""unbounded"" ref=""DATA"" />
617 </xs:sequence>
618 </xs:complexType>
619 </xs:element>
620 <xs:element name=""integer"" type=""xs:string"" />
621 <xs:element name=""string"">
622 <xs:complexType>
623 <xs:simpleContent>
624 <xs:extension base=""xs:string"">
625 <xs:attribute ref=""xml:space"" />
626 </xs:extension>
627 </xs:simpleContent>
628 </xs:complexType>
629 </xs:element>
630 </xs:schema>
631 ";
632 #endregion XSD
633  
634 MemoryStream stream = new MemoryStream(Encoding.ASCII.GetBytes(schemaText));
635  
636 XmlSchema = new XmlSchema();
637 XmlSchema = XmlSchema.Read(stream, new ValidationEventHandler(LLSDXmlSchemaValidationHandler));
638 }
639 }
640  
641 private static void LLSDXmlSchemaValidationHandler(object sender, ValidationEventArgs args)
642 {
643 string error = String.Format("Line: {0} - Position: {1} - {2}", XmlTextReader.LineNumber, XmlTextReader.LinePosition,
644 args.Message);
645  
646 if (LastXmlErrors == String.Empty)
647 LastXmlErrors = error;
648 else
649 LastXmlErrors += Environment.NewLine + error;
650 }
651 }
652 }