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