From 1c6b8293d7b9e312c5b1fb544a1de7fd3b145670 Mon Sep 17 00:00:00 2001 From: Melanie Date: Tue, 7 May 2013 00:52:40 +0100 Subject: Step 3: Commit the Avination XEstate estate comms handler This adds estate-wide Teleport Home and Teleport All User Home as well --- .../CoreModules/World/Estate/XEstateConnector.cs | 215 +++++++++++++++ .../CoreModules/World/Estate/XEstateModule.cs | 254 ++++++++++++++++++ .../World/Estate/XEstateRequestHandler.cs | 298 +++++++++++++++++++++ 3 files changed, 767 insertions(+) create mode 100644 OpenSim/Region/CoreModules/World/Estate/XEstateConnector.cs create mode 100644 OpenSim/Region/CoreModules/World/Estate/XEstateModule.cs create mode 100644 OpenSim/Region/CoreModules/World/Estate/XEstateRequestHandler.cs diff --git a/OpenSim/Region/CoreModules/World/Estate/XEstateConnector.cs b/OpenSim/Region/CoreModules/World/Estate/XEstateConnector.cs new file mode 100644 index 0000000..948c893 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Estate/XEstateConnector.cs @@ -0,0 +1,215 @@ +/* + * 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. + */ + +using System; +using System.Collections.Generic; +using System.Reflection; + +using OpenSim.Services.Interfaces; +using GridRegion = OpenSim.Services.Interfaces.GridRegion; +using OpenSim.Server.Base; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Framework; +using OpenSim.Region.Framework.Scenes; + +using OpenMetaverse; +using log4net; + +namespace OpenSim.Region.CoreModules.World.Estate +{ + public class EstateConnector + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected XEstateModule m_EstateModule; + + public EstateConnector(XEstateModule module) + { + m_EstateModule = module; + } + + public void SendTeleportHomeOneUser(uint EstateID, UUID PreyID) + { + Dictionary sendData = new Dictionary(); + sendData["METHOD"] = "teleport_home_one_user"; + + sendData["EstateID"] = EstateID.ToString(); + sendData["PreyID"] = PreyID.ToString(); + + SendToEstate(EstateID, sendData); + } + + public void SendTeleportHomeAllUsers(uint EstateID) + { + Dictionary sendData = new Dictionary(); + sendData["METHOD"] = "teleport_home_all_users"; + + sendData["EstateID"] = EstateID.ToString(); + + SendToEstate(EstateID, sendData); + } + + public bool SendUpdateCovenant(uint EstateID, UUID CovenantID) + { + Dictionary sendData = new Dictionary(); + sendData["METHOD"] = "update_covenant"; + + sendData["CovenantID"] = CovenantID.ToString(); + sendData["EstateID"] = EstateID.ToString(); + + // Handle local regions locally + // + foreach (Scene s in m_EstateModule.Scenes) + { + if (s.RegionInfo.EstateSettings.EstateID == EstateID) + s.RegionInfo.RegionSettings.Covenant = CovenantID; +// s.ReloadEstateData(); + } + + SendToEstate(EstateID, sendData); + + return true; + } + + public bool SendUpdateEstate(uint EstateID) + { + Dictionary sendData = new Dictionary(); + sendData["METHOD"] = "update_estate"; + + sendData["EstateID"] = EstateID.ToString(); + + // Handle local regions locally + // + foreach (Scene s in m_EstateModule.Scenes) + { + if (s.RegionInfo.EstateSettings.EstateID == EstateID) + s.ReloadEstateData(); + } + + SendToEstate(EstateID, sendData); + + return true; + } + + public void SendEstateMessage(uint EstateID, UUID FromID, string FromName, string Message) + { + Dictionary sendData = new Dictionary(); + sendData["METHOD"] = "estate_message"; + + sendData["EstateID"] = EstateID.ToString(); + sendData["FromID"] = FromID.ToString(); + sendData["FromName"] = FromName; + sendData["Message"] = Message; + + SendToEstate(EstateID, sendData); + } + + private void SendToEstate(uint EstateID, Dictionary sendData) + { + List regions = m_EstateModule.Scenes[0].GetEstateRegions((int)EstateID); + + UUID ScopeID = UUID.Zero; + + // Handle local regions locally + // + foreach (Scene s in m_EstateModule.Scenes) + { + if (regions.Contains(s.RegionInfo.RegionID)) + { + // All regions in one estate are in the same scope. + // Use that scope. + // + ScopeID = s.RegionInfo.ScopeID; + regions.Remove(s.RegionInfo.RegionID); + } + } + + // Our own region should always be in the above list. + // In a standalone this would not be true. But then, + // Scope ID is not relevat there. Use first scope. + // + if (ScopeID == UUID.Zero) + ScopeID = m_EstateModule.Scenes[0].RegionInfo.ScopeID; + + // Don't send to the same instance twice + // + List done = new List(); + + // Send to remote regions + // + foreach (UUID regionID in regions) + { + GridRegion region = m_EstateModule.Scenes[0].GridService.GetRegionByUUID(ScopeID, regionID); + if (region != null) + { + string url = "http://" + region.ExternalHostName + ":" + region.HttpPort; + if (done.Contains(url)) + continue; + + Call(region, sendData); + done.Add(url); + } + } + } + + private bool Call(GridRegion region, Dictionary sendData) + { + string reqString = ServerUtils.BuildQueryString(sendData); + // m_log.DebugFormat("[XESTATE CONNECTOR]: queryString = {0}", reqString); + try + { + string url = "http://" + region.ExternalHostName + ":" + region.HttpPort; + string reply = SynchronousRestFormsRequester.MakeRequest("POST", + url + "/estate", + reqString); + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if (replyData.ContainsKey("RESULT")) + { + if (replyData["RESULT"].ToString().ToLower() == "true") + return true; + else + return false; + } + else + m_log.DebugFormat("[XESTATE CONNECTOR]: reply data does not contain result field"); + + } + else + m_log.DebugFormat("[XESTATE CONNECTOR]: received empty reply"); + } + catch (Exception e) + { + m_log.DebugFormat("[XESTATE CONNECTOR]: Exception when contacting remote sim: {0}", e.Message); + } + + return false; + } + } +} diff --git a/OpenSim/Region/CoreModules/World/Estate/XEstateModule.cs b/OpenSim/Region/CoreModules/World/Estate/XEstateModule.cs new file mode 100644 index 0000000..1f099c6 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Estate/XEstateModule.cs @@ -0,0 +1,254 @@ +/* + * 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. + */ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using log4net; +using Nini.Config; +using Nwc.XmlRpc; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Framework.Communications; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; +using OpenSim.Server.Base; +using OpenSim.Framework.Servers; +using OpenSim.Framework.Servers.HttpServer; +using Mono.Addins; + +namespace OpenSim.Region.CoreModules.World.Estate +{ + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "XEstate")] + public class XEstateModule : ISharedRegionModule + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected List m_Scenes = new List(); + protected bool m_InInfoUpdate = false; + + public bool InInfoUpdate + { + get { return m_InInfoUpdate; } + set { m_InInfoUpdate = value; } + } + + public List Scenes + { + get { return m_Scenes; } + } + + protected EstateConnector m_EstateConnector; + + public void Initialise(IConfigSource config) + { + int port = 0; + + IConfig estateConfig = config.Configs["Estate"]; + if (estateConfig != null) + { + port = estateConfig.GetInt("Port", 0); + } + + m_EstateConnector = new EstateConnector(this); + + // Instantiate the request handler + IHttpServer server = MainServer.GetHttpServer((uint)port); + server.AddStreamHandler(new EstateRequestHandler(this)); + } + + public void PostInitialise() + { + } + + public void Close() + { + } + + public void AddRegion(Scene scene) + { + m_Scenes.Add(scene); + + scene.EventManager.OnNewClient += OnNewClient; + } + + public void RegionLoaded(Scene scene) + { + IEstateModule em = scene.RequestModuleInterface(); + + em.OnRegionInfoChange += OnRegionInfoChange; + em.OnEstateInfoChange += OnEstateInfoChange; + em.OnEstateMessage += OnEstateMessage; + } + + public void RemoveRegion(Scene scene) + { + scene.EventManager.OnNewClient -= OnNewClient; + + m_Scenes.Remove(scene); + } + + public string Name + { + get { return "EstateModule"; } + } + + public Type ReplaceableInterface + { + get { return null; } + } + + private Scene FindScene(UUID RegionID) + { + foreach (Scene s in Scenes) + { + if (s.RegionInfo.RegionID == RegionID) + return s; + } + + return null; + } + + private void OnRegionInfoChange(UUID RegionID) + { + Scene s = FindScene(RegionID); + if (s == null) + return; + + if (!m_InInfoUpdate) + m_EstateConnector.SendUpdateCovenant(s.RegionInfo.EstateSettings.EstateID, s.RegionInfo.RegionSettings.Covenant); + } + + private void OnEstateInfoChange(UUID RegionID) + { + Scene s = FindScene(RegionID); + if (s == null) + return; + + if (!m_InInfoUpdate) + m_EstateConnector.SendUpdateEstate(s.RegionInfo.EstateSettings.EstateID); + } + + private void OnEstateMessage(UUID RegionID, UUID FromID, string FromName, string Message) + { + Scene senderScenes = FindScene(RegionID); + if (senderScenes == null) + return; + + uint estateID = senderScenes.RegionInfo.EstateSettings.EstateID; + + foreach (Scene s in Scenes) + { + if (s.RegionInfo.EstateSettings.EstateID == estateID) + { + IDialogModule dm = s.RequestModuleInterface(); + + if (dm != null) + { + dm.SendNotificationToUsersInRegion(FromID, FromName, + Message); + } + } + } + if (!m_InInfoUpdate) + m_EstateConnector.SendEstateMessage(estateID, FromID, FromName, Message); + } + + private void OnNewClient(IClientAPI client) + { + client.OnEstateTeleportOneUserHomeRequest += OnEstateTeleportOneUserHomeRequest; + client.OnEstateTeleportAllUsersHomeRequest += OnEstateTeleportAllUsersHomeRequest; + + } + + private void OnEstateTeleportOneUserHomeRequest(IClientAPI client, UUID invoice, UUID senderID, UUID prey) + { + if (prey == UUID.Zero) + return; + + if (!(client.Scene is Scene)) + return; + + Scene scene = (Scene)client.Scene; + + uint estateID = scene.RegionInfo.EstateSettings.EstateID; + + if (!scene.Permissions.CanIssueEstateCommand(client.AgentId, false)) + return; + + foreach (Scene s in Scenes) + { + if (s == scene) + continue; // Already handles by estate module + if (s.RegionInfo.EstateSettings.EstateID != estateID) + continue; + + ScenePresence p = scene.GetScenePresence(prey); + if (p != null && !p.IsChildAgent) + { + p.ControllingClient.SendTeleportStart(16); + scene.TeleportClientHome(prey, p.ControllingClient); + } + } + + m_EstateConnector.SendTeleportHomeOneUser(estateID, prey); + } + + private void OnEstateTeleportAllUsersHomeRequest(IClientAPI client, UUID invoice, UUID senderID) + { + if (!(client.Scene is Scene)) + return; + + Scene scene = (Scene)client.Scene; + + uint estateID = scene.RegionInfo.EstateSettings.EstateID; + + if (!scene.Permissions.CanIssueEstateCommand(client.AgentId, false)) + return; + + foreach (Scene s in Scenes) + { + if (s == scene) + continue; // Already handles by estate module + if (s.RegionInfo.EstateSettings.EstateID != estateID) + continue; + + scene.ForEachScenePresence(delegate(ScenePresence p) { + if (p != null && !p.IsChildAgent) + { + p.ControllingClient.SendTeleportStart(16); + scene.TeleportClientHome(p.ControllingClient.AgentId, p.ControllingClient); + } + }); + } + + m_EstateConnector.SendTeleportHomeAllUsers(estateID); + } + } +} diff --git a/OpenSim/Region/CoreModules/World/Estate/XEstateRequestHandler.cs b/OpenSim/Region/CoreModules/World/Estate/XEstateRequestHandler.cs new file mode 100644 index 0000000..eb74cda --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Estate/XEstateRequestHandler.cs @@ -0,0 +1,298 @@ +/* + * 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. + */ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Xml; + +using OpenSim.Framework; +using OpenSim.Server.Base; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.Framework.Interfaces; + +using OpenMetaverse; +using log4net; + +namespace OpenSim.Region.CoreModules.World.Estate +{ + public class EstateRequestHandler : BaseStreamHandler + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected XEstateModule m_EstateModule; + protected Object m_RequestLock = new Object(); + + public EstateRequestHandler(XEstateModule fmodule) + : base("POST", "/estate") + { + m_EstateModule = fmodule; + } + + public override byte[] Handle(string path, Stream requestData, + IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + StreamReader sr = new StreamReader(requestData); + string body = sr.ReadToEnd(); + sr.Close(); + body = body.Trim(); + + m_log.DebugFormat("[XESTATE HANDLER]: query String: {0}", body); + + try + { + lock (m_RequestLock) + { + Dictionary request = + ServerUtils.ParseQueryString(body); + + if (!request.ContainsKey("METHOD")) + return FailureResult(); + + string method = request["METHOD"].ToString(); + request.Remove("METHOD"); + + try + { + m_EstateModule.InInfoUpdate = false; + + switch (method) + { + case "update_covenant": + return UpdateCovenant(request); + case "update_estate": + return UpdateEstate(request); + case "estate_message": + return EstateMessage(request); + case "teleport_home_one_user": + return TeleportHomeOneUser(request); + case "teleport_home_all_users": + return TeleportHomeAllUsers(request); + } + } + finally + { + m_EstateModule.InInfoUpdate = false; + } + } + } + catch (Exception e) + { + m_log.Debug("[XESTATE]: Exception {0}" + e.ToString()); + } + + return FailureResult(); + } + + byte[] TeleportHomeAllUsers(Dictionary request) + { + UUID PreyID = UUID.Zero; + int EstateID = 0; + + if (!request.ContainsKey("EstateID")) + return FailureResult(); + + if (!Int32.TryParse(request["EstateID"].ToString(), out EstateID)) + return FailureResult(); + + foreach (Scene s in m_EstateModule.Scenes) + { + if (s.RegionInfo.EstateSettings.EstateID == EstateID) + { + s.ForEachScenePresence(delegate(ScenePresence p) { + if (p != null && !p.IsChildAgent) + { + p.ControllingClient.SendTeleportStart(16); + s.TeleportClientHome(p.ControllingClient.AgentId, p.ControllingClient); + } + }); + } + } + + return SuccessResult(); + } + + byte[] TeleportHomeOneUser(Dictionary request) + { + UUID PreyID = UUID.Zero; + int EstateID = 0; + + if (!request.ContainsKey("PreyID") || + !request.ContainsKey("EstateID")) + { + return FailureResult(); + } + + if (!UUID.TryParse(request["PreyID"].ToString(), out PreyID)) + return FailureResult(); + + if (!Int32.TryParse(request["EstateID"].ToString(), out EstateID)) + return FailureResult(); + + foreach (Scene s in m_EstateModule.Scenes) + { + if (s.RegionInfo.EstateSettings.EstateID == EstateID) + { + ScenePresence p = s.GetScenePresence(PreyID); + if (p != null && !p.IsChildAgent) + { + p.ControllingClient.SendTeleportStart(16); + s.TeleportClientHome(PreyID, p.ControllingClient); + } + } + } + + return SuccessResult(); + } + + byte[] EstateMessage(Dictionary request) + { + UUID FromID = UUID.Zero; + string FromName = String.Empty; + string Message = String.Empty; + int EstateID = 0; + + if (!request.ContainsKey("FromID") || + !request.ContainsKey("FromName") || + !request.ContainsKey("Message") || + !request.ContainsKey("EstateID")) + { + return FailureResult(); + } + + if (!UUID.TryParse(request["FromID"].ToString(), out FromID)) + return FailureResult(); + + if (!Int32.TryParse(request["EstateID"].ToString(), out EstateID)) + return FailureResult(); + + FromName = request["FromName"].ToString(); + Message = request["Message"].ToString(); + + foreach (Scene s in m_EstateModule.Scenes) + { + if (s.RegionInfo.EstateSettings.EstateID == EstateID) + { + IDialogModule dm = s.RequestModuleInterface(); + + if (dm != null) + { + dm.SendNotificationToUsersInRegion(FromID, FromName, + Message); + } + } + } + + return SuccessResult(); + } + + byte[] UpdateCovenant(Dictionary request) + { + UUID CovenantID = UUID.Zero; + int EstateID = 0; + + if (!request.ContainsKey("CovenantID") || !request.ContainsKey("EstateID")) + return FailureResult(); + + if (!UUID.TryParse(request["CovenantID"].ToString(), out CovenantID)) + return FailureResult(); + + if (!Int32.TryParse(request["EstateID"].ToString(), out EstateID)) + return FailureResult(); + + foreach (Scene s in m_EstateModule.Scenes) + { + if (s.RegionInfo.EstateSettings.EstateID == (uint)EstateID) + s.RegionInfo.RegionSettings.Covenant = CovenantID; + } + + return SuccessResult(); + } + + byte[] UpdateEstate(Dictionary request) + { + int EstateID = 0; + + if (!request.ContainsKey("EstateID")) + return FailureResult(); + if (!Int32.TryParse(request["EstateID"].ToString(), out EstateID)) + return FailureResult(); + + foreach (Scene s in m_EstateModule.Scenes) + { + if (s.RegionInfo.EstateSettings.EstateID == (uint)EstateID) + s.ReloadEstateData(); + } + return SuccessResult(); + } + + private byte[] FailureResult() + { + return BoolResult(false); + } + + private byte[] SuccessResult() + { + return BoolResult(true); + } + + private byte[] BoolResult(bool value) + { + XmlDocument doc = new XmlDocument(); + + XmlNode xmlnode = doc.CreateNode(XmlNodeType.XmlDeclaration, + "", ""); + + doc.AppendChild(xmlnode); + + XmlElement rootElement = doc.CreateElement("", "ServerResponse", + ""); + + doc.AppendChild(rootElement); + + XmlElement result = doc.CreateElement("", "RESULT", ""); + result.AppendChild(doc.CreateTextNode(value.ToString())); + + rootElement.AppendChild(result); + + return DocToBytes(doc); + } + + private byte[] DocToBytes(XmlDocument doc) + { + MemoryStream ms = new MemoryStream(); + XmlTextWriter xw = new XmlTextWriter(ms, null); + xw.Formatting = Formatting.Indented; + doc.WriteTo(xw); + xw.Flush(); + + return ms.ToArray(); + } + } +} -- cgit v1.1