clockwerk-opensim – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 vero 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;
30 using System.Globalization;
31 using System.IO;
32 using System.Security.Cryptography;
33 using System.Text;
34 using System.Xml;
35 using OpenMetaverse;
36  
37 namespace OpenSim.Framework.Capabilities
38 {
39 /// <summary>
40 /// Borrowed from (a older version of) libsl for now, as their new llsd code doesn't work we our decoding code.
41 /// </summary>
42 public static class LLSD
43 {
44 /// <summary>
45 ///
46 /// </summary>
47 public class LLSDParseException : Exception
48 {
49 public LLSDParseException(string message) : base(message)
50 {
51 }
52 }
53  
54 /// <summary>
55 ///
56 /// </summary>
57 public class LLSDSerializeException : Exception
58 {
59 public LLSDSerializeException(string message) : base(message)
60 {
61 }
62 }
63  
64 /// <summary>
65 ///
66 /// </summary>
67 /// <param name="b"></param>
68 /// <returns></returns>
69 public static object LLSDDeserialize(byte[] b)
70 {
71 using (MemoryStream ms = new MemoryStream(b, false))
72 {
73 return LLSDDeserialize(ms);
74 }
75 }
76  
77 /// <summary>
78 ///
79 /// </summary>
80 /// <param name="st"></param>
81 /// <returns></returns>
82 public static object LLSDDeserialize(Stream st)
83 {
84 using (XmlTextReader reader = new XmlTextReader(st))
85 {
86 reader.Read();
87 SkipWS(reader);
88  
89 if (reader.NodeType != XmlNodeType.Element || reader.LocalName != "llsd")
90 throw new LLSDParseException("Expected <llsd>");
91  
92 reader.Read();
93 object ret = LLSDParseOne(reader);
94 SkipWS(reader);
95  
96 if (reader.NodeType != XmlNodeType.EndElement || reader.LocalName != "llsd")
97 throw new LLSDParseException("Expected </llsd>");
98  
99 return ret;
100 }
101 }
102  
103 /// <summary>
104 ///
105 /// </summary>
106 /// <param name="obj"></param>
107 /// <returns></returns>
108 public static byte[] LLSDSerialize(object obj)
109 {
110 StringWriter sw = new StringWriter();
111 XmlTextWriter writer = new XmlTextWriter(sw);
112 writer.Formatting = Formatting.None;
113  
114 writer.WriteStartElement(String.Empty, "llsd", String.Empty);
115 LLSDWriteOne(writer, obj);
116 writer.WriteEndElement();
117  
118 writer.Close();
119  
120 return Util.UTF8.GetBytes(sw.ToString());
121 }
122  
123 /// <summary>
124 ///
125 /// </summary>
126 /// <param name="writer"></param>
127 /// <param name="obj"></param>
128 public static void LLSDWriteOne(XmlTextWriter writer, object obj)
129 {
130 if (obj == null)
131 {
132 writer.WriteStartElement(String.Empty, "undef", String.Empty);
133 writer.WriteEndElement();
134 return;
135 }
136  
137 if (obj is string)
138 {
139 writer.WriteStartElement(String.Empty, "string", String.Empty);
140 writer.WriteString((string) obj);
141 writer.WriteEndElement();
142 }
143 else if (obj is int)
144 {
145 writer.WriteStartElement(String.Empty, "integer", String.Empty);
146 writer.WriteString(obj.ToString());
147 writer.WriteEndElement();
148 }
149 else if (obj is double)
150 {
151 writer.WriteStartElement(String.Empty, "real", String.Empty);
152 writer.WriteString(obj.ToString());
153 writer.WriteEndElement();
154 }
155 else if (obj is bool)
156 {
157 bool b = (bool) obj;
158 writer.WriteStartElement(String.Empty, "boolean", String.Empty);
159 writer.WriteString(b ? "1" : "0");
160 writer.WriteEndElement();
161 }
162 else if (obj is ulong)
163 {
164 throw new Exception("ulong in LLSD is currently not implemented, fix me!");
165 }
166 else if (obj is UUID)
167 {
168 UUID u = (UUID) obj;
169 writer.WriteStartElement(String.Empty, "uuid", String.Empty);
170 writer.WriteString(u.ToString());
171 writer.WriteEndElement();
172 }
173 else if (obj is Hashtable)
174 {
175 Hashtable h = obj as Hashtable;
176 writer.WriteStartElement(String.Empty, "map", String.Empty);
177 foreach (string key in h.Keys)
178 {
179 writer.WriteStartElement(String.Empty, "key", String.Empty);
180 writer.WriteString(key);
181 writer.WriteEndElement();
182 LLSDWriteOne(writer, h[key]);
183 }
184 writer.WriteEndElement();
185 }
186 else if (obj is ArrayList)
187 {
188 ArrayList a = obj as ArrayList;
189 writer.WriteStartElement(String.Empty, "array", String.Empty);
190 foreach (object item in a)
191 {
192 LLSDWriteOne(writer, item);
193 }
194 writer.WriteEndElement();
195 }
196 else if (obj is byte[])
197 {
198 byte[] b = obj as byte[];
199 writer.WriteStartElement(String.Empty, "binary", String.Empty);
200  
201 writer.WriteStartAttribute(String.Empty, "encoding", String.Empty);
202 writer.WriteString("base64");
203 writer.WriteEndAttribute();
204  
205 //// Calculate the length of the base64 output
206 //long length = (long)(4.0d * b.Length / 3.0d);
207 //if (length % 4 != 0) length += 4 - (length % 4);
208  
209 //// Create the char[] for base64 output and fill it
210 //char[] tmp = new char[length];
211 //int i = Convert.ToBase64CharArray(b, 0, b.Length, tmp, 0);
212  
213 //writer.WriteString(new String(tmp));
214  
215 writer.WriteString(Convert.ToBase64String(b));
216 writer.WriteEndElement();
217 }
218 else
219 {
220 throw new LLSDSerializeException("Unknown type " + obj.GetType().Name);
221 }
222 }
223  
224 /// <summary>
225 ///
226 /// </summary>
227 /// <param name="reader"></param>
228 /// <returns></returns>
229 public static object LLSDParseOne(XmlTextReader reader)
230 {
231 SkipWS(reader);
232 if (reader.NodeType != XmlNodeType.Element)
233 throw new LLSDParseException("Expected an element");
234  
235 string dtype = reader.LocalName;
236 object ret = null;
237  
238 switch (dtype)
239 {
240 case "undef":
241 {
242 if (reader.IsEmptyElement)
243 {
244 reader.Read();
245 return null;
246 }
247  
248 reader.Read();
249 SkipWS(reader);
250 ret = null;
251 break;
252 }
253 case "boolean":
254 {
255 if (reader.IsEmptyElement)
256 {
257 reader.Read();
258 return false;
259 }
260  
261 reader.Read();
262 string s = reader.ReadString().Trim();
263  
264 if (s == String.Empty || s == "false" || s == "0")
265 ret = false;
266 else if (s == "true" || s == "1")
267 ret = true;
268 else
269 throw new LLSDParseException("Bad boolean value " + s);
270  
271 break;
272 }
273 case "integer":
274 {
275 if (reader.IsEmptyElement)
276 {
277 reader.Read();
278 return 0;
279 }
280  
281 reader.Read();
282 ret = Convert.ToInt32(reader.ReadString().Trim());
283 break;
284 }
285 case "real":
286 {
287 if (reader.IsEmptyElement)
288 {
289 reader.Read();
290 return 0.0f;
291 }
292  
293 reader.Read();
294 ret = Convert.ToDouble(reader.ReadString().Trim());
295 break;
296 }
297 case "uuid":
298 {
299 if (reader.IsEmptyElement)
300 {
301 reader.Read();
302 return UUID.Zero;
303 }
304  
305 reader.Read();
306 ret = new UUID(reader.ReadString().Trim());
307 break;
308 }
309 case "string":
310 {
311 if (reader.IsEmptyElement)
312 {
313 reader.Read();
314 return String.Empty;
315 }
316  
317 reader.Read();
318 ret = reader.ReadString();
319 break;
320 }
321 case "binary":
322 {
323 if (reader.IsEmptyElement)
324 {
325 reader.Read();
326 return new byte[0];
327 }
328  
329 if (reader.GetAttribute("encoding") != null &&
330 reader.GetAttribute("encoding") != "base64")
331 {
332 throw new LLSDParseException("Unknown encoding: " + reader.GetAttribute("encoding"));
333 }
334  
335 reader.Read();
336 FromBase64Transform b64 = new FromBase64Transform(FromBase64TransformMode.IgnoreWhiteSpaces);
337 byte[] inp = Util.UTF8.GetBytes(reader.ReadString());
338 ret = b64.TransformFinalBlock(inp, 0, inp.Length);
339 break;
340 }
341 case "date":
342 {
343 reader.Read();
344 throw new Exception("LLSD TODO: date");
345 }
346 case "map":
347 {
348 return LLSDParseMap(reader);
349 }
350 case "array":
351 {
352 return LLSDParseArray(reader);
353 }
354 default:
355 throw new LLSDParseException("Unknown element <" + dtype + ">");
356 }
357  
358 if (reader.NodeType != XmlNodeType.EndElement || reader.LocalName != dtype)
359 {
360 throw new LLSDParseException("Expected </" + dtype + ">");
361 }
362  
363 reader.Read();
364 return ret;
365 }
366  
367 /// <summary>
368 ///
369 /// </summary>
370 /// <param name="reader"></param>
371 /// <returns></returns>
372 public static Hashtable LLSDParseMap(XmlTextReader reader)
373 {
374 Hashtable ret = new Hashtable();
375  
376 if (reader.NodeType != XmlNodeType.Element || reader.LocalName != "map")
377 throw new LLSDParseException("Expected <map>");
378  
379 if (reader.IsEmptyElement)
380 {
381 reader.Read();
382 return ret;
383 }
384  
385 reader.Read();
386  
387 while (true)
388 {
389 SkipWS(reader);
390 if (reader.NodeType == XmlNodeType.EndElement && reader.LocalName == "map")
391 {
392 reader.Read();
393 break;
394 }
395  
396 if (reader.NodeType != XmlNodeType.Element || reader.LocalName != "key")
397 throw new LLSDParseException("Expected <key>");
398  
399 string key = reader.ReadString();
400  
401 if (reader.NodeType != XmlNodeType.EndElement || reader.LocalName != "key")
402 throw new LLSDParseException("Expected </key>");
403  
404 reader.Read();
405 object val = LLSDParseOne(reader);
406 ret[key] = val;
407 }
408  
409 return ret; // TODO
410 }
411  
412 /// <summary>
413 ///
414 /// </summary>
415 /// <param name="reader"></param>
416 /// <returns></returns>
417 public static ArrayList LLSDParseArray(XmlTextReader reader)
418 {
419 ArrayList ret = new ArrayList();
420  
421 if (reader.NodeType != XmlNodeType.Element || reader.LocalName != "array")
422 throw new LLSDParseException("Expected <array>");
423  
424 if (reader.IsEmptyElement)
425 {
426 reader.Read();
427 return ret;
428 }
429  
430 reader.Read();
431  
432 while (true)
433 {
434 SkipWS(reader);
435  
436 if (reader.NodeType == XmlNodeType.EndElement && reader.LocalName == "array")
437 {
438 reader.Read();
439 break;
440 }
441  
442 ret.Insert(ret.Count, LLSDParseOne(reader));
443 }
444  
445 return ret; // TODO
446 }
447  
448 /// <summary>
449 ///
450 /// </summary>
451 /// <param name="count"></param>
452 /// <returns></returns>
453 private static string GetSpaces(int count)
454 {
455 StringBuilder b = new StringBuilder();
456 for (int i = 0; i < count; i++) b.Append(" ");
457 return b.ToString();
458 }
459  
460 /// <summary>
461 ///
462 /// </summary>
463 /// <param name="obj"></param>
464 /// <param name="indent"></param>
465 /// <returns></returns>
466 public static String LLSDDump(object obj, int indent)
467 {
468 if (obj == null)
469 {
470 return GetSpaces(indent) + "- undef\n";
471 }
472 else if (obj is string)
473 {
474 return GetSpaces(indent) + "- string \"" + (string) obj + "\"\n";
475 }
476 else if (obj is int)
477 {
478 return GetSpaces(indent) + "- integer " + obj.ToString() + "\n";
479 }
480 else if (obj is double)
481 {
482 return GetSpaces(indent) + "- float " + obj.ToString() + "\n";
483 }
484 else if (obj is UUID)
485 {
486 return GetSpaces(indent) + "- uuid " + ((UUID) obj).ToString() + Environment.NewLine;
487 }
488 else if (obj is Hashtable)
489 {
490 StringBuilder ret = new StringBuilder();
491 ret.Append(GetSpaces(indent) + "- map" + Environment.NewLine);
492 Hashtable map = (Hashtable) obj;
493  
494 foreach (string key in map.Keys)
495 {
496 ret.Append(GetSpaces(indent + 2) + "- key \"" + key + "\"" + Environment.NewLine);
497 ret.Append(LLSDDump(map[key], indent + 3));
498 }
499  
500 return ret.ToString();
501 }
502 else if (obj is ArrayList)
503 {
504 StringBuilder ret = new StringBuilder();
505 ret.Append(GetSpaces(indent) + "- array\n");
506 ArrayList list = (ArrayList) obj;
507  
508 foreach (object item in list)
509 {
510 ret.Append(LLSDDump(item, indent + 2));
511 }
512  
513 return ret.ToString();
514 }
515 else if (obj is byte[])
516 {
517 return GetSpaces(indent) + "- binary\n" + Utils.BytesToHexString((byte[]) obj, GetSpaces(indent)) +
518 Environment.NewLine;
519 }
520 else
521 {
522 return GetSpaces(indent) + "- unknown type " + obj.GetType().Name + Environment.NewLine;
523 }
524 }
525  
526 public static object ParseTerseLLSD(string llsd)
527 {
528 int notused;
529 return ParseTerseLLSD(llsd, out notused);
530 }
531  
532 public static object ParseTerseLLSD(string llsd, out int endPos)
533 {
534 if (llsd.Length == 0)
535 {
536 endPos = 0;
537 return null;
538 }
539  
540 // Identify what type of object this is
541 switch (llsd[0])
542 {
543 case '!':
544 throw new LLSDParseException("Undefined value type encountered");
545 case '1':
546 endPos = 1;
547 return true;
548 case '0':
549 endPos = 1;
550 return false;
551 case 'i':
552 {
553 if (llsd.Length < 2) throw new LLSDParseException("Integer value type with no value");
554 int value;
555 endPos = FindEnd(llsd, 1);
556  
557 if (Int32.TryParse(llsd.Substring(1, endPos - 1), out value))
558 return value;
559 else
560 throw new LLSDParseException("Failed to parse integer value type");
561 }
562 case 'r':
563 {
564 if (llsd.Length < 2) throw new LLSDParseException("Real value type with no value");
565 double value;
566 endPos = FindEnd(llsd, 1);
567  
568 if (Double.TryParse(llsd.Substring(1, endPos - 1), NumberStyles.Float,
569 Utils.EnUsCulture.NumberFormat, out value))
570 return value;
571 else
572 throw new LLSDParseException("Failed to parse double value type");
573 }
574 case 'u':
575 {
576 if (llsd.Length < 17) throw new LLSDParseException("UUID value type with no value");
577 UUID value;
578 endPos = FindEnd(llsd, 1);
579  
580 if (UUID.TryParse(llsd.Substring(1, endPos - 1), out value))
581 return value;
582 else
583 throw new LLSDParseException("Failed to parse UUID value type");
584 }
585 case 'b':
586 //byte[] value = new byte[llsd.Length - 1];
587 // This isn't the actual binary LLSD format, just the terse format sent
588 // at login so I don't even know if there is a binary type
589 throw new LLSDParseException("Binary value type is unimplemented");
590 case 's':
591 case 'l':
592 if (llsd.Length < 2) throw new LLSDParseException("String value type with no value");
593 endPos = FindEnd(llsd, 1);
594 return llsd.Substring(1, endPos - 1);
595 case 'd':
596 // Never seen one before, don't know what the format is
597 throw new LLSDParseException("Date value type is unimplemented");
598 case '[':
599 {
600 if (llsd.IndexOf(']') == -1) throw new LLSDParseException("Invalid array");
601  
602 int pos = 0;
603 ArrayList array = new ArrayList();
604  
605 while (llsd[pos] != ']')
606 {
607 ++pos;
608  
609 // Advance past comma if need be
610 if (llsd[pos] == ',') ++pos;
611  
612 // Allow a single whitespace character
613 if (pos < llsd.Length && llsd[pos] == ' ') ++pos;
614  
615 int end;
616 array.Add(ParseTerseLLSD(llsd.Substring(pos), out end));
617 pos += end;
618 }
619  
620 endPos = pos + 1;
621 return array;
622 }
623 case '{':
624 {
625 if (llsd.IndexOf('}') == -1) throw new LLSDParseException("Invalid map");
626  
627 int pos = 0;
628 Hashtable hashtable = new Hashtable();
629  
630 while (llsd[pos] != '}')
631 {
632 ++pos;
633  
634 // Advance past comma if need be
635 if (llsd[pos] == ',') ++pos;
636  
637 // Allow a single whitespace character
638 if (pos < llsd.Length && llsd[pos] == ' ') ++pos;
639  
640 if (llsd[pos] != '\'') throw new LLSDParseException("Expected a map key");
641 int endquote = llsd.IndexOf('\'', pos + 1);
642 if (endquote == -1 || (endquote + 1) >= llsd.Length || llsd[endquote + 1] != ':')
643 throw new LLSDParseException("Invalid map format");
644 string key = llsd.Substring(pos, endquote - pos);
645 key = key.Replace("'", String.Empty);
646 pos += (endquote - pos) + 2;
647  
648 int end;
649 hashtable.Add(key, ParseTerseLLSD(llsd.Substring(pos), out end));
650 pos += end;
651 }
652  
653 endPos = pos + 1;
654 return hashtable;
655 }
656 default:
657 throw new Exception("Unknown value type");
658 }
659 }
660  
661 private static int FindEnd(string llsd, int start)
662 {
663 int end = llsd.IndexOfAny(new char[] {',', ']', '}'});
664 if (end == -1) end = llsd.Length - 1;
665 return end;
666 }
667  
668 /// <summary>
669 ///
670 /// </summary>
671 /// <param name="reader"></param>
672 private static void SkipWS(XmlTextReader reader)
673 {
674 while (
675 reader.NodeType == XmlNodeType.Comment ||
676 reader.NodeType == XmlNodeType.Whitespace ||
677 reader.NodeType == XmlNodeType.SignificantWhitespace ||
678 reader.NodeType == XmlNodeType.XmlDeclaration)
679 {
680 reader.Read();
681 }
682 }
683 }
684 }