diff --git a/OpenSim/Framework/WebUtil.cs b/OpenSim/Framework/WebUtil.cs index 94862a6372..3624b3b952 100644 --- a/OpenSim/Framework/WebUtil.cs +++ b/OpenSim/Framework/WebUtil.cs @@ -40,6 +40,39 @@ using OpenMetaverse.StructuredData; namespace OpenSim.Framework { + /// + /// A generic HTTP request handler + /// + public class HttpStreamHandler : IStreamHandler + { + public delegate void HttpStreamCallback(OSHttpRequest httpRequest, OSHttpResponse httpResponse); + + private string m_contentType; + private string m_httpMethod; + private string m_path; + private HttpStreamCallback m_callback; + + public string ContentType { get { return m_contentType; } } + public string HttpMethod { get { return m_httpMethod; } } + public string Path { get { return m_path; } } + + /// + /// Constructor + /// + public HttpStreamHandler(string contentType, string httpMethod, string path, HttpStreamCallback callback) + { + m_contentType = contentType; + m_httpMethod = httpMethod; + m_path = path; + m_callback = callback; + } + + public void Handle(string path, Stream request, Stream response, OSHttpRequest httpRequest, OSHttpResponse httpResponse) + { + m_callback(httpRequest, httpResponse); + } + } + /// /// Miscellaneous static methods and extension methods related to the web /// @@ -176,6 +209,16 @@ namespace OpenSim.Framework return new OSDMap { { "Message", OSD.FromString("Service request failed. " + errorMessage) } }; } + /// + /// Encodes a URL string + /// + /// Unencoded string to encode + /// URL-encoded string + public static string UrlEncode(string str) + { + return HttpUtility.UrlEncode(str).Replace("+", "%20"); + } + #region Uri /// diff --git a/OpenSim/Services/Connectors/SimianGrid/RezAvatar.cs b/OpenSim/Services/Connectors/SimianGrid/RezAvatar.cs new file mode 100644 index 0000000000..334ab6b90b --- /dev/null +++ b/OpenSim/Services/Connectors/SimianGrid/RezAvatar.cs @@ -0,0 +1,283 @@ +/* + * 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.Collections.Specialized; +using System.Net; +using System.Reflection; +using log4net; +using Mono.Addins; +using Nini.Config; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using OpenSim.Framework; +using OpenSim.Framework.Client; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; +using OpenSim.Framework.Servers.HttpServer; + +using GridRegion = OpenSim.Services.Interfaces.GridRegion; + +namespace OpenSim.Services.Connectors.SimianGrid +{ + /// + /// Handles logins and teleports + /// + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")] + public class RezAvatar : INonSharedRegionModule + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private Scene m_scene; + private GridRegion m_gridRegion; + private ISimulationService m_simulationService; + + #region INonSharedRegionModule + + public Type ReplaceableInterface { get { return null; } } + public void RegionLoaded(Scene scene) { } + public void Close() { } + + public RezAvatar() { } + public string Name { get { return "RezAvatar"; } } + + public void AddRegion(Scene scene) + { + m_scene = scene; + m_gridRegion = new GridRegion + { + RegionID = scene.RegionInfo.RegionID, + RegionLocX = (int)scene.RegionInfo.RegionLocX, + RegionLocY = (int)scene.RegionInfo.RegionLocY, + RegionName = scene.RegionInfo.RegionName + }; + + if (Simian.IsSimianEnabled(scene.Config, "UserAccountServices", "SimianUserAccountServiceConnector")) + { + m_simulationService = scene.RequestModuleInterface(); + if (m_simulationService != null) + m_simulationService = m_simulationService.GetInnerService(); + + string urlFriendlySceneName = WebUtil.UrlEncode(scene.RegionInfo.RegionName); + + MainServer.Instance.AddStreamHandler(new HttpStreamHandler("application/xml+llsd", "GET", "/scenes/" + urlFriendlySceneName + "/public_region_seed_capability", + PublicRegionSeedCapabilityHandler)); + MainServer.Instance.AddStreamHandler(new HttpStreamHandler(null, "POST", "/scenes/" + urlFriendlySceneName + "/rez_avatar/request", + RezAvatarRequestHandler)); + } + } + + public void RemoveRegion(Scene scene) + { + if (Simian.IsSimianEnabled(scene.Config, "UserAccountServices", this.Name)) + { + string urlFriendlySceneName = WebUtil.UrlEncode(scene.RegionInfo.RegionName); + + MainServer.Instance.RemoveStreamHandler("GET", "/scenes/" + urlFriendlySceneName + "/public_region_seed_capability"); + MainServer.Instance.RemoveStreamHandler("GET", "/scenes/" + urlFriendlySceneName + "/rez_avatar/request"); + } + + m_scene = null; + } + + #endregion INonSharedRegionModule + + public RezAvatar(IConfigSource source) + { + } + + public void Initialise(IConfigSource source) + { + } + + private void PublicRegionSeedCapabilityHandler(OSHttpRequest httpRequest, OSHttpResponse httpResponse) + { + // Build a seed capability response that contains the rez_avatar/request capability (a hardcoded URL) + OSDMap responseMap = new OSDMap(); + + Uri httpAddress = new Uri("http://" + m_scene.RegionInfo.ExternalHostName + ":" + m_scene.RegionInfo.HttpPort + "/"); + string urlFriendlySceneName = WebUtil.UrlEncode(m_scene.RegionInfo.RegionName); + + OSDMap capabilities = new OSDMap(); + capabilities["rez_avatar/request"] = OSD.FromUri(new Uri(httpAddress, "/scenes/" + urlFriendlySceneName + "/rez_avatar/request")); + + responseMap["capabilities"] = capabilities; + + WebUtil.SendJSONResponse(httpResponse, responseMap); + } + + private void RezAvatarRequestHandler(OSHttpRequest httpRequest, OSHttpResponse httpResponse) + { + OSDMap requestMap = null; + try { requestMap = OSDParser.Deserialize(httpRequest.InputStream) as OSDMap; } + catch { } + + Vector3 lookAt = Vector3.UnitX; + RegionInfo regInfo = m_scene.RegionInfo; + + // Unpack the rez_avatar/request into an AgentCircuitData structure + AgentCircuitData agentCircuit = BuildAgentCircuitFromRezAvatarRequest(requestMap); + + OSDMap responseMap = new OSDMap(); + + if (agentCircuit != null && agentCircuit.AgentID != UUID.Zero) + { + if (agentCircuit.startpos == Vector3.Zero) + { + m_log.Info("rez_avatar/request did not contain a position, setting to default"); + agentCircuit.startpos = new Vector3(128f, 128f, 25f); + } + + // Attempt to create an agent from the AgentCircuitData info + string reason; + if (m_simulationService.CreateAgent(m_gridRegion, agentCircuit, agentCircuit.teleportFlags, out reason)) + { + // Build the seed capability URL for this agent + Uri seedCapability = new Uri("http://" + regInfo.ExternalHostName + ":" + regInfo.HttpPort + "/CAPS/" + agentCircuit.CapsPath + "0000/"); + + m_log.Info("rez_avatar/request created agent " + agentCircuit.firstname + " " + agentCircuit.lastname + + " with circuit code " + agentCircuit.circuitcode + " and seed capability " + seedCapability); + + IPAddress externalAddress = regInfo.ExternalEndPoint.Address; + uint regionX, regionY; + Utils.LongToUInts(regInfo.RegionHandle, out regionX, out regionY); + + responseMap["connect"] = OSD.FromBoolean(true); + responseMap["agent_id"] = OSD.FromUUID(agentCircuit.AgentID); + responseMap["sim_host"] = OSD.FromString(externalAddress.ToString()); + responseMap["sim_port"] = OSD.FromInteger(regInfo.ExternalEndPoint.Port); + responseMap["region_seed_capability"] = OSD.FromUri(seedCapability); + responseMap["position"] = OSD.FromVector3(agentCircuit.startpos); + responseMap["look_at"] = OSD.FromVector3(lookAt); + + // Region information + responseMap["region_id"] = OSD.FromUUID(regInfo.RegionID); + responseMap["region_x"] = OSD.FromInteger(regionX); + responseMap["region_y"] = OSD.FromInteger(regionY); + } + else + { + m_log.Warn("Denied rez_avatar/request for " + agentCircuit.firstname + " " + agentCircuit.lastname + ": " + reason); + responseMap["message"] = OSD.FromString(reason); + } + } + else + { + responseMap["message"] = OSD.FromString("Invalid or incomplete request"); + } + + WebUtil.SendJSONResponse(httpResponse, responseMap); + } + + private static AgentCircuitData BuildAgentCircuitFromRezAvatarRequest(OSDMap request) + { + AgentCircuitData agentCircuit = new AgentCircuitData(); + agentCircuit.AgentID = request["agent_id"].AsUUID(); + agentCircuit.BaseFolder = request["base_folder"].AsUUID(); + agentCircuit.CapsPath = request["caps_path"].AsString(); + agentCircuit.child = request["child_agent"].AsBoolean(); + agentCircuit.circuitcode = request["circuit_code"].AsUInteger(); + agentCircuit.firstname = request["first_name"].AsString(); + agentCircuit.InventoryFolder = request["inventory_folder"].AsUUID(); + agentCircuit.lastname = request["last_name"].AsString(); + agentCircuit.SecureSessionID = request["secure_session_id"].AsUUID(); + agentCircuit.ServiceSessionID = String.Empty; + agentCircuit.ServiceURLs = new Dictionary(0); + agentCircuit.SessionID = request["session_id"].AsUUID(); + agentCircuit.startpos = request["position"].AsVector3(); + agentCircuit.teleportFlags = request["teleport_flags"].AsUInteger(); + if (agentCircuit.teleportFlags == 0) + agentCircuit.teleportFlags = (uint)TeleportFlags.ViaLogin; + + #region Children Seed Caps + + if (request["children_seeds"].Type == OSDType.Array) + { + OSDArray childrenSeeds = (OSDArray)request["children_seeds"]; + agentCircuit.ChildrenCapSeeds = new Dictionary(childrenSeeds.Count); + for (int i = 0; i < childrenSeeds.Count; i++) + { + if (childrenSeeds[i].Type == OSDType.Map) + { + OSDMap pair = (OSDMap)childrenSeeds[i]; + ulong handle; + if (!UInt64.TryParse(pair["handle"].AsString(), out handle)) + continue; + string seed = pair["seed"].AsString(); + if (!agentCircuit.ChildrenCapSeeds.ContainsKey(handle)) + agentCircuit.ChildrenCapSeeds.Add(handle, seed); + } + } + } + else + { + agentCircuit.ChildrenCapSeeds = new Dictionary(0); + } + + #endregion Children Seed Caps + + #region Appearance + + agentCircuit.Appearance = new AvatarAppearance(agentCircuit.AgentID); + agentCircuit.Appearance.Serial = request["appearance_serial"].AsInteger(); + if (request["wearables"].Type == OSDType.Array) + { + OSDArray wearables = (OSDArray)request["wearables"]; + for (int i = 0; i < wearables.Count / 2; i++) + { + agentCircuit.Appearance.Wearables[i].ItemID = wearables[i * 2].AsUUID(); + agentCircuit.Appearance.Wearables[i].AssetID = wearables[(i * 2) + 1].AsUUID(); + } + } + + if (request["attachments"].Type == OSDType.Array) + { + OSDArray attachArray = (OSDArray)request["attachments"]; + List attachments = new List(attachArray.Count); + for (int i = 0; i < attachArray.Count; i++) + { + if (attachArray[i].Type == OSDType.Map) + attachments.Add(new AttachmentData((OSDMap)attachArray[i])); + } + agentCircuit.Appearance.SetAttachments(attachments.ToArray()); + } + + #endregion Appearance + + return agentCircuit; + } + + private static OSDMap BuildRezAvatarRequestFromAgentCircuit(AgentCircuitData agentCircuit) + { + return new OSDMap(); + } + } +} diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianProfiles.cs b/OpenSim/Services/Connectors/SimianGrid/SimianProfiles.cs index fbf4648155..1457b869ad 100644 --- a/OpenSim/Services/Connectors/SimianGrid/SimianProfiles.cs +++ b/OpenSim/Services/Connectors/SimianGrid/SimianProfiles.cs @@ -88,7 +88,7 @@ namespace OpenSim.Services.Connectors.SimianGrid public void Initialise(IConfigSource source) { - if (Simian.IsSimianEnabled(source, "UserAccountServices", this.Name)) + if (Simian.IsSimianEnabled(source, "UserAccountServices", "SimianUserAccountServiceConnector")) { IConfig gridConfig = source.Configs["UserAccountService"]; if (gridConfig == null)