/* * Copyright (c) Contributors, http://opensimulator.org/ * See CONTRIBUTORS.TXT for a full list of copyright holders. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the OpenSimulator Project nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // a class for low level LLSD encoding into a provided StringBuilder // for cases where we already need to know the low level detail // and so using something like OSD or even protbuf is just a pure waste using System; using System.Globalization; using System.Text; using OpenMetaverse; namespace OpenSim.Framework { public static class LLSDxmlEncode { static readonly DateTime depoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); public static void AddStart(StringBuilder sb, bool addxmlversion = false) { if(addxmlversion) sb.Append(""); // legacy llsd xml name still valid else sb.Append(""); } public const string LLSDEmpty = ""; // got tired of creating a stringbuilder all the time; public static StringBuilder Start(int size = 4096, bool addxmlversion = false) { StringBuilder sb = osStringBuilderCache.Acquire(size); if(addxmlversion) sb.Append(""); // legacy llsd xml name still valid else sb.Append(""); return sb; } public static void AddEnd(StringBuilder sb) { sb.Append(""); } public static string End(StringBuilder sb) { sb.Append(""); return osStringBuilderCache.GetStringAndRelease(sb); } public static byte[] EndToNBBytes(StringBuilder sb) { sb.Append(""); return Util.UTF8NBGetbytes(osStringBuilderCache.GetStringAndRelease(sb)); } // map == a list of key value pairs public static void AddMap(StringBuilder sb) { sb.Append(""); } public static void AddEndMap(StringBuilder sb) { sb.Append(""); } public static void AddEmptyMap(StringBuilder sb) { sb.Append(""); } // array == a list values public static void AddArray(StringBuilder sb) { sb.Append(""); } public static void AddEndArray(StringBuilder sb) { sb.Append(""); } public static void AddEndMapAndArray(StringBuilder sb) { sb.Append(""); } public static void AddEmptyArray(StringBuilder sb) { sb.Append(""); } // undefined or null public static void AddUnknownElem(StringBuilder sb) { sb.Append(""); } public static void AddElem(bool e, StringBuilder sb) { if(e) sb.Append("1"); else sb.Append("0"); } public static void AddElem(byte e, StringBuilder sb) { if(e == 0) sb.Append(""); else { sb.Append(""); sb.Append(e.ToString()); sb.Append(""); } } public static void AddElem(byte[] e, StringBuilder sb) { if(e == null || e.Length == 0) sb.Append("binary />"); else { sb.Append(""); // encode64 is default base64Encode(e, sb); sb.Append(""); } } public static void AddElem(byte[] e, int start, int length, StringBuilder sb) { if (start + length >= e.Length) length = e.Length - start; if (e == null || e.Length == 0 || length <= 0) sb.Append("binary />"); else { sb.Append(""); // encode64 is default base64Encode(e, start, length, sb); sb.Append(""); } } public static void AddElem(int e, StringBuilder sb) { if(e == 0) sb.Append(""); else { sb.Append(""); sb.Append(e.ToString()); sb.Append(""); } } public static void AddElem(uint e, StringBuilder sb) { AddElem(uintToByteArray(e), sb); } public static void AddElem(ulong e, StringBuilder sb) { AddElem(ulongToByteArray(e), sb); } public static void AddElem(float e, StringBuilder sb) { if(e == 0) sb.Append("0"); else { sb.Append(""); sb.Append(e.ToString(CultureInfo.InvariantCulture)); sb.Append(""); } } public static void AddElem(Vector2 e, StringBuilder sb) { sb.Append(""); if(e.X == 0) sb.Append("0"); else { sb.Append(""); sb.Append(e.X.ToString(CultureInfo.InvariantCulture)); sb.Append(""); } if(e.Y == 0) sb.Append("0"); else { sb.Append(""); sb.Append(e.Y.ToString(CultureInfo.InvariantCulture)); sb.Append(""); } } public static void AddElem(Vector3 e, StringBuilder sb) { sb.Append(""); if(e.X == 0) sb.Append("0"); else { sb.Append(""); sb.Append(e.X.ToString(CultureInfo.InvariantCulture)); sb.Append(""); } if(e.Y == 0) sb.Append("0"); else { sb.Append(""); sb.Append(e.Y.ToString(CultureInfo.InvariantCulture)); sb.Append(""); } if(e.Z == 0) sb.Append("0"); else { sb.Append(""); sb.Append(e.Z.ToString(CultureInfo.InvariantCulture)); sb.Append(""); } } public static void AddElem(Quaternion e, StringBuilder sb) { sb.Append(""); if(e.X == 0) sb.Append("0"); else { sb.Append(""); sb.Append(e.X.ToString(CultureInfo.InvariantCulture)); sb.Append(""); } if(e.Y == 0) sb.Append("0"); else { sb.Append(""); sb.Append(e.Y.ToString(CultureInfo.InvariantCulture)); sb.Append(""); } if(e.Z == 0) sb.Append("0"); else { sb.Append(""); sb.Append(e.Z.ToString(CultureInfo.InvariantCulture)); sb.Append(""); } if(e.W == 0) sb.Append("0"); else { sb.Append(""); sb.Append(e.W.ToString(CultureInfo.InvariantCulture)); sb.Append(""); } } public static void AddElem(double e, StringBuilder sb) { if(e == 0) sb.Append("0"); else { sb.Append(""); sb.Append(e.ToString(CultureInfo.InvariantCulture)); sb.Append(""); } } public static void AddElem(UUID e, StringBuilder sb) { if(e.IsZero()) sb.Append(""); else { sb.Append(""); EscapeToXML(e.ToString(), sb); sb.Append(""); } } public static void AddElem(string e, StringBuilder sb) { if(String.IsNullOrEmpty(e)) sb.Append(""); else { sb.Append(""); EscapeToXML(e, sb); sb.Append(""); } } public static void AddRawElem(string e, StringBuilder sb) { if(String.IsNullOrEmpty(e)) return; sb.Append(e); } public static void AddElem(Uri e, StringBuilder sb) { if(e == null) { sb.Append(""); return; } string s; if (e.IsAbsoluteUri) s = e.AbsoluteUri; else s = e.ToString(); if(String.IsNullOrEmpty(s)) sb.Append(""); else { sb.Append(""); sb.Append(s); sb.Append(""); } } public static void AddElem(DateTime e, StringBuilder sb) { DateTime u = e.ToUniversalTime(); if(u == depoch) { sb.Append(""); return; } string format; if(u.Hour == 0 && u.Minute == 0 && u.Second == 0) format = "yyyy-MM-dd"; else if (u.Millisecond > 0) format = "yyyy-MM-ddTHH:mm:ss.ffZ"; else format = "yyyy-MM-ddTHH:mm:ssZ"; sb.Append(""); sb.Append(u.ToString(format,CultureInfo.InvariantCulture)); sb.Append(""); } //************ key value ******************* // assumes name is a valid llsd key public static void AddMap(string name, StringBuilder sb) { sb.Append(""); sb.Append(name); sb.Append(""); } public static void AddEmptyMap(string name, StringBuilder sb) { sb.Append(""); sb.Append(name); sb.Append(""); } // array == a list values public static void AddArray(string name, StringBuilder sb) { sb.Append(""); sb.Append(name); sb.Append(""); } public static void AddArrayAndMap(string name, StringBuilder sb) { sb.Append(""); sb.Append(name); sb.Append(""); } public static void AddEmptyArray(string name, StringBuilder sb) { sb.Append(""); sb.Append(name); sb.Append(""); } // undefined or null public static void AddUnknownElem(string name, StringBuilder sb) { sb.Append(""); sb.Append(name); sb.Append(""); } public static void AddElem(string name, bool e, StringBuilder sb) { sb.Append(""); sb.Append(name); sb.Append(""); if(e) sb.Append("1"); else sb.Append("0"); } public static void AddElem(string name, byte e, StringBuilder sb) { sb.Append(""); sb.Append(name); sb.Append(""); if(e == 0) sb.Append(""); else { sb.Append(""); sb.Append(e.ToString()); sb.Append(""); } } public static void AddElem(string name, byte[] e, StringBuilder sb) { sb.Append(""); sb.Append(name); sb.Append(""); if (e == null || e.Length == 0) sb.Append("binary />"); else { sb.Append(""); // encode64 is default base64Encode(e, sb); sb.Append(""); } } public static void AddElem(string name, byte[] e, int start, int length, StringBuilder sb) { sb.Append(""); sb.Append(name); sb.Append(""); if (start + length >= e.Length) length = e.Length - start; if (e == null || e.Length == 0 || length <= 0) sb.Append("binary />"); else { sb.Append(""); // encode64 is default base64Encode(e, start, length, sb); sb.Append(""); } } public static void AddElem(string name, int e, StringBuilder sb) { sb.Append(""); sb.Append(name); sb.Append(""); if(e == 0) sb.Append(""); else { sb.Append(""); sb.Append(e.ToString()); sb.Append(""); } } public static void AddElem(string name, uint e, StringBuilder sb) { AddElem(name, uintToByteArray(e), sb); } public static void AddElem(string name, ulong e, StringBuilder sb) { AddElem(name, ulongToByteArray(e), sb); } public static void AddElem(string name, float e, StringBuilder sb) { sb.Append(""); sb.Append(name); sb.Append(""); if(e == 0) sb.Append("0"); else { sb.Append(""); sb.Append(e.ToString(CultureInfo.InvariantCulture)); sb.Append(""); } } public static void AddElem(string name, Vector2 e, StringBuilder sb) { sb.Append(""); sb.Append(name); sb.Append(">"); if(e.X == 0) sb.Append("0"); else { sb.Append(""); sb.Append(e.X.ToString(CultureInfo.InvariantCulture)); sb.Append(""); } if(e.Y == 0) sb.Append("0"); else { sb.Append(""); sb.Append(e.Y.ToString(CultureInfo.InvariantCulture)); sb.Append(""); } } public static void AddElem(string name, Vector3 e, StringBuilder sb) { sb.Append(""); sb.Append(name); sb.Append(""); if(e.X == 0) sb.Append("0"); else { sb.Append(""); sb.Append(e.X.ToString(CultureInfo.InvariantCulture)); sb.Append(""); } if(e.Y == 0) sb.Append("0"); else { sb.Append(""); sb.Append(e.Y.ToString(CultureInfo.InvariantCulture)); sb.Append(""); } if(e.Z == 0) sb.Append("0"); else { sb.Append(""); sb.Append(e.Z.ToString(CultureInfo.InvariantCulture)); sb.Append(""); } } public static void AddElem(string name, Quaternion e, StringBuilder sb) { sb.Append(""); sb.Append(name); sb.Append(""); if(e.X == 0) sb.Append("0"); else { sb.Append(""); sb.Append(e.X.ToString(CultureInfo.InvariantCulture)); sb.Append(""); } if(e.Y == 0) sb.Append("0"); else { sb.Append(""); sb.Append(e.Y.ToString(CultureInfo.InvariantCulture)); sb.Append(""); } if(e.Z == 0) sb.Append("0"); else { sb.Append(""); sb.Append(e.Z.ToString(CultureInfo.InvariantCulture)); sb.Append(""); } if(e.W == 0) sb.Append("0"); else { sb.Append(""); sb.Append(e.W.ToString(CultureInfo.InvariantCulture)); sb.Append(""); } } public static void AddElem(string name, double e, StringBuilder sb) { sb.Append(""); sb.Append(name); sb.Append(""); if(e == 0) sb.Append("0"); else { sb.Append(""); sb.Append(e.ToString(CultureInfo.InvariantCulture)); sb.Append(""); } } public static void AddElem(string name, UUID e, StringBuilder sb) { sb.Append(""); sb.Append(name); sb.Append(""); if(e.IsZero()) sb.Append(""); else { sb.Append(""); EscapeToXML(e.ToString(), sb); sb.Append(""); } } public static void AddElem(string name, string e, StringBuilder sb) { sb.Append(""); sb.Append(name); sb.Append(""); if(String.IsNullOrEmpty(e)) sb.Append(""); else { sb.Append(""); EscapeToXML(e, sb); sb.Append(""); } } public static void AddRawElem(string name, string e, StringBuilder sb) { if (String.IsNullOrEmpty(e)) return; sb.Append(""); sb.Append(name); sb.Append(""); sb.Append(e); } public static void AddElem(string name, Uri e, StringBuilder sb) { sb.Append(""); sb.Append(name); sb.Append(""); if(e == null) { sb.Append(""); return; } string s; if (e.IsAbsoluteUri) s = e.AbsoluteUri; else s = e.ToString(); if(String.IsNullOrEmpty(s)) sb.Append(""); else { sb.Append(""); sb.Append(s); sb.Append(""); } } public static void AddElem(string name, DateTime e, StringBuilder sb) { sb.Append(""); sb.Append(name); sb.Append(""); DateTime u = e.ToUniversalTime(); if(u == depoch) { sb.Append(""); return; } string format; if(u.Hour == 0 && u.Minute == 0 && u.Second == 0) format = "yyyy-MM-dd"; else if (u.Millisecond > 0) format = "yyyy-MM-ddTHH:mm:ss.ffZ"; else format = "yyyy-MM-ddTHH:mm:ssZ"; sb.Append(""); sb.Append(u.ToString(format,CultureInfo.InvariantCulture)); sb.Append(""); } public static void AddLLSD(string e, StringBuilder sb) { sb.Append(e); } public static void AddLLSD(string name, string e, StringBuilder sb) { sb.Append(""); sb.Append(name); sb.Append(""); sb.Append(e); } public static void EscapeToXML(string s, StringBuilder sb) { char c; for (int i = 0; i < s.Length; ++i) { c = s[i]; switch (c) { case '<': sb.Append("<"); break; case '>': sb.Append(">"); break; case '&': sb.Append("&"); break; case '"': sb.Append("""); break; case '\\': sb.Append("'"); break; default: sb.Append(c); break; } } } public static byte[] ulongToByteArray(ulong uLongValue) { return new byte[8] { (byte)(uLongValue >> 56), (byte)(uLongValue >> 48), (byte)(uLongValue >> 40), (byte)(uLongValue >> 32), (byte)(uLongValue >> 24), (byte)(uLongValue >> 16), (byte)(uLongValue >> 8), (byte)uLongValue }; } public static byte[] uintToByteArray(uint value) { return new byte[4] { (byte)(value >> 24), (byte)(value >> 16), (byte)(value >> 8), (byte)value }; } static readonly char[] base64Chars = {'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O', 'P','Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d', 'e','f','g','h','i','j','k','l','m','n','o','p','q','r','s', 't','u','v','w','x','y','z','0','1','2','3','4','5','6','7', '8','9','+','/'}; public static unsafe void base64Encode(byte[] data, StringBuilder sb) { int lenMod3 = data.Length % 3; int len = data.Length - lenMod3; fixed (byte* d = data) { fixed (char* b64 = base64Chars) { int i = 0; while(i < len) { sb.Append(b64[d[i] >> 2]); sb.Append(b64[((d[i] & 0x03) << 4) | ((d[i + 1] & 0xf0) >> 4)]); sb.Append(b64[((d[i + 1] & 0x0f) << 2) | ((d[i + 2] & 0xc0) >> 6)]); sb.Append(b64[d[i + 2] & 0x3f]); i += 3; } switch (lenMod3) { case 2: { i = len; sb.Append(b64[d[i] >> 2]); sb.Append(b64[((d[i] & 0x03) << 4) | ((d[i + 1] & 0xf0) >> 4)]); sb.Append(b64[((d[i + 1] & 0x0f) << 2)]); sb.Append('='); break; } case 1: { i = len; sb.Append(b64[d[i] >> 2]); sb.Append(b64[(d[i] & 0x03) << 4]); sb.Append("=="); break; } } } } } public static unsafe void base64Encode(byte[] data, int start, int length, StringBuilder sb) { int lenMod3 = length % 3; int len = start + (length - lenMod3); fixed (byte* d = data) { fixed (char* b64 = base64Chars) { int i = start; while(i < len) { sb.Append(b64[(d[i] & 0xfc) >> 2]); sb.Append(b64[((d[i] & 0x03) << 4) | (d[i + 1] >> 4)]); sb.Append(b64[((d[i + 1] & 0x0f) << 2) | (d[i + 2] >> 6)]); sb.Append(b64[d[i + 2] & 0x3f]); i += 3; } switch (lenMod3) { case 2: { i = len; sb.Append(b64[d[i] >> 2]); sb.Append(b64[((d[i] & 0x03) << 4) | (d[i + 1] >> 4)]); sb.Append(b64[((d[i + 1] & 0x0f) << 2)]); sb.Append('='); break; } case 1: { i = len; sb.Append(b64[d[i] >> 2]); sb.Append(b64[(d[i] & 0x03) << 4]); sb.Append("=="); break; } } } } } } }