From c0d3ab59e66649bda446b8bcec9f01db053f1fcb Mon Sep 17 00:00:00 2001 From: Melanie Date: Tue, 7 Dec 2010 15:24:17 +0000 Subject: [PATCH 1/6] Remove old Freeswitch configurations --- bin/OpenSimDefaults.ini | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/bin/OpenSimDefaults.ini b/bin/OpenSimDefaults.ini index f98689b613..0c21c66b6c 100644 --- a/bin/OpenSimDefaults.ini +++ b/bin/OpenSimDefaults.ini @@ -1179,39 +1179,6 @@ ; - a value between 0 and 160, default is 10 ;vivox_channel_clamping_distance = 10 -[FreeSwitchVoice] - ; In order for this to work you need a functioning FreeSWITCH PBX set up. - ; Configuration details at http://opensimulator.org/wiki/Freeswitch_Module - enabled = false - ; FreeSWITCH server is going to contact us and ask us all sorts of things - freeswitch_server_user = freeswitch - freeswitch_server_pass = password - freeswitch_api_prefix = /api - ; external IP address of your OpenSim voice enabled region - ; note: all regions running on same OpenSim.exe will be enabled - freeswitch_service_server = ip.address.of.your.sim - ; this should be the same port the region listens on - freeswitch_service_port = 9000 - freeswitch_realm = ip.address.of.freeswitch.server - freeswitch_sip_proxy = ip.address.of.freeswitch.server:5060 - ; STUN = Simple Traversal of UDP through NATs - ; See http://wiki.freeswitch.org/wiki/NAT_Traversal - ; stun.freeswitch.org is not guaranteed to be running so use it in production at your own risk - freeswitch_attempt_stun = false - freeswitch_stun_server = ip.address.of.stun.server - freeswitch_echo_server = ip.address.of.freeswitch.server - freeswitch_echo_port = 50505 - freeswitch_well_known_ip = ip.address.of.freeswitch.server - ; - ; Type the address of your http server here, hostname is allowed. This is provided so you can specify a hostname - ; This is used by client for account verification. By default, it's the same as the freeswitch service server. - ; - ; opensim_well_known_http_address = Address_Of_Your_SIM_HTTP_Server_Hostname_Allowed - ; - freeswitch_default_timeout = 5000 - freeswitch_subscribe_retry = 120 - ; freeswitch_password_reset_url = - [Groups] Enabled = false From 1fa9399ab86afc126deefafe7277419aaea6f5e1 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Wed, 8 Dec 2010 00:41:33 +0000 Subject: [PATCH 2/6] change doc. trivial commit to get panda to rebuild --- OpenSim/Region/Framework/Scenes/Scene.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 9ade100b14..6d72154d73 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -4246,7 +4246,7 @@ namespace OpenSim.Region.Framework.Scenes } /// - /// + /// Perform the given action for each object /// /// // public void ForEachObject(Action action) From 7cfb3d7e96a1340d59a5a6984f46ba4fd9e21a0f Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Tue, 7 Dec 2010 17:17:06 -0800 Subject: [PATCH 3/6] Removed extraneous left-/ on /object/ subpath. Bug introduced during region URLs clean up. --- .../Connectors/Simulation/SimulationServiceConnector.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Services/Connectors/Simulation/SimulationServiceConnector.cs b/OpenSim/Services/Connectors/Simulation/SimulationServiceConnector.cs index 585a30e195..3b907c1736 100644 --- a/OpenSim/Services/Connectors/Simulation/SimulationServiceConnector.cs +++ b/OpenSim/Services/Connectors/Simulation/SimulationServiceConnector.cs @@ -489,7 +489,7 @@ namespace OpenSim.Services.Connectors.Simulation protected virtual string ObjectPath() { - return "/object/"; + return "object/"; } public bool CreateObject(GridRegion destination, ISceneObject sog, bool isLocalCall) From 796216e44ff78b1088f9a977b810f16d98855f37 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Tue, 7 Dec 2010 20:05:53 -0800 Subject: [PATCH 4/6] Added an exception handler on CreateObject handler, just in case there's an exception being thrown that is silently being ignored by the http server. (Trying to catch Melanie's problem with attachments on TPs) --- .../Handlers/Simulation/ObjectHandlers.cs | 51 +++++++++++-------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/OpenSim/Server/Handlers/Simulation/ObjectHandlers.cs b/OpenSim/Server/Handlers/Simulation/ObjectHandlers.cs index 04ff83f9f3..984b843401 100644 --- a/OpenSim/Server/Handlers/Simulation/ObjectHandlers.cs +++ b/OpenSim/Server/Handlers/Simulation/ObjectHandlers.cs @@ -84,32 +84,43 @@ namespace OpenSim.Server.Handlers.Simulation return responsedata; } - // Next, let's parse the verb - string method = (string)request["http-method"]; - if (method.Equals("POST")) + try { - DoObjectPost(request, responsedata, regionID); - return responsedata; + // Next, let's parse the verb + string method = (string)request["http-method"]; + if (method.Equals("POST")) + { + DoObjectPost(request, responsedata, regionID); + return responsedata; + } + else if (method.Equals("PUT")) + { + DoObjectPut(request, responsedata, regionID); + return responsedata; + } + //else if (method.Equals("DELETE")) + //{ + // DoObjectDelete(request, responsedata, agentID, action, regionHandle); + // return responsedata; + //} + else + { + m_log.InfoFormat("[OBJECT HANDLER]: method {0} not supported in object message", method); + responsedata["int_response_code"] = HttpStatusCode.MethodNotAllowed; + responsedata["str_response_string"] = "Method not allowed"; + + return responsedata; + } } - else if (method.Equals("PUT")) + catch (Exception e) { - DoObjectPut(request, responsedata, regionID); - return responsedata; - } - //else if (method.Equals("DELETE")) - //{ - // DoObjectDelete(request, responsedata, agentID, action, regionHandle); - // return responsedata; - //} - else - { - m_log.InfoFormat("[OBJECT HANDLER]: method {0} not supported in object message", method); - responsedata["int_response_code"] = HttpStatusCode.MethodNotAllowed; - responsedata["str_response_string"] = "Mthod not allowed"; + m_log.WarnFormat("[OBJECT HANDLER]: Caught exception {0}", e.StackTrace); + responsedata["int_response_code"] = HttpStatusCode.InternalServerError; + responsedata["str_response_string"] = "Internal server error"; return responsedata; - } + } } protected void DoObjectPost(Hashtable request, Hashtable responsedata, UUID regionID) From 4df1d25d23d76ee3ae8c7da155462b57735f6418 Mon Sep 17 00:00:00 2001 From: Melanie Date: Thu, 9 Dec 2010 01:55:32 +0000 Subject: [PATCH 5/6] Plumb a code path for the entity transfer module to ask a destination scene whether or not an agent is allowed there as a root agent. --- .../Simulation/LocalSimulationConnector.cs | 17 ++++++ .../Simulation/RemoteSimulationConnector.cs | 17 ++++++ OpenSim/Region/Framework/Scenes/Scene.cs | 11 ++++ .../Handlers/Simulation/AgentHandlers.cs | 26 ++++++++ .../Simulation/SimulationServiceConnector.cs | 59 +++++++++++++++++++ .../Services/Interfaces/ISimulationService.cs | 2 + 6 files changed, 132 insertions(+) diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Simulation/LocalSimulationConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Simulation/LocalSimulationConnector.cs index e32dbb3bfd..37b403e8cc 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Simulation/LocalSimulationConnector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Simulation/LocalSimulationConnector.cs @@ -257,6 +257,23 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Simulation return false; } + public bool QueryAccess(GridRegion destination, UUID id) + { + if (destination == null) + return false; + + foreach (Scene s in m_sceneList) + { + if (s.RegionInfo.RegionHandle == destination.RegionHandle) + { + //m_log.Debug("[LOCAL COMMS]: Found region to send QueryAccess"); + return s.QueryAccess(id); + } + } + //m_log.Debug("[LOCAL COMMS]: region not found for QueryAccess"); + return false; + } + public bool ReleaseAgent(UUID origin, UUID id, string uri) { foreach (Scene s in m_sceneList) diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Simulation/RemoteSimulationConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Simulation/RemoteSimulationConnector.cs index 3f577f2ad6..387a9b8133 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Simulation/RemoteSimulationConnector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Simulation/RemoteSimulationConnector.cs @@ -239,6 +239,23 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Simulation } + public bool QueryAccess(GridRegion destination, UUID id) + { + if (destination == null) + return false; + + // Try local first + if (m_localBackend.QueryAccess(destination, id)) + return true; + + // else do the remote thing + if (!m_localBackend.IsLocalRegion(destination.RegionHandle)) + return m_remoteConnector.QueryAccess(destination, id); + + return false; + + } + public bool ReleaseAgent(UUID origin, UUID id, string uri) { // Try local first diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 6d72154d73..de753755a1 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -4953,5 +4953,16 @@ namespace OpenSim.Region.Framework.Scenes DeleteSceneObject(grp, true); } } + + // This method is called across the simulation connector to + // determine if a given agent is allowed in this region + // AS A ROOT AGENT. Returning false here will prevent them + // from logging into the region, teleporting into the region + // or corssing the broder walking, but will NOT prevent + // child agent creation, thereby emulating the SL behavior. + public bool QueryAccess(UUID agentID) + { + return true; + } } } diff --git a/OpenSim/Server/Handlers/Simulation/AgentHandlers.cs b/OpenSim/Server/Handlers/Simulation/AgentHandlers.cs index 1f7e50217f..24ae81ff0f 100644 --- a/OpenSim/Server/Handlers/Simulation/AgentHandlers.cs +++ b/OpenSim/Server/Handlers/Simulation/AgentHandlers.cs @@ -110,6 +110,11 @@ namespace OpenSim.Server.Handlers.Simulation DoAgentDelete(request, responsedata, agentID, action, regionID); return responsedata; } + else if (method.Equals("QUERYACCESSS")) + { + DoQueryAccess(request, responsedata, agentID, regionID); + return responsedata; + } else { m_log.InfoFormat("[AGENT HANDLER]: method {0} not supported in agent message", method); @@ -300,6 +305,27 @@ namespace OpenSim.Server.Handlers.Simulation return m_SimulationService.UpdateAgent(destination, agent); } + protected virtual void DoQueryAccess(Hashtable request, Hashtable responsedata, UUID id, UUID regionID) + { + if (m_SimulationService == null) + { + m_log.Debug("[AGENT HANDLER]: Agent QUERY called. Harmless but useless."); + responsedata["content_type"] = "application/json"; + responsedata["int_response_code"] = HttpStatusCode.NotImplemented; + responsedata["str_response_string"] = string.Empty; + + return; + } + + GridRegion destination = new GridRegion(); + destination.RegionID = regionID; + + bool result = m_SimulationService.QueryAccess(destination, id); + + responsedata["int_response_code"] = HttpStatusCode.OK; + responsedata["str_response_string"] = result.ToString(); + } + protected virtual void DoAgentGet(Hashtable request, Hashtable responsedata, UUID id, UUID regionID) { if (m_SimulationService == null) diff --git a/OpenSim/Services/Connectors/Simulation/SimulationServiceConnector.cs b/OpenSim/Services/Connectors/Simulation/SimulationServiceConnector.cs index 3b907c1736..e2032d9200 100644 --- a/OpenSim/Services/Connectors/Simulation/SimulationServiceConnector.cs +++ b/OpenSim/Services/Connectors/Simulation/SimulationServiceConnector.cs @@ -409,6 +409,65 @@ namespace OpenSim.Services.Connectors.Simulation return false; } + public bool QueryAccess(GridRegion destination, UUID id) + { + IPEndPoint ext = destination.ExternalEndPoint; + if (ext == null) return false; + // Eventually, we want to use a caps url instead of the agentID + string uri = destination.ServerURI + AgentPath() + id + "/" + destination.RegionID.ToString() + "/"; + + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri); + request.Method = "QUERYACCESS"; + request.Timeout = 10000; + //request.Headers.Add("authorization", ""); // coming soon + + HttpWebResponse webResponse = null; + string reply = string.Empty; + StreamReader sr = null; + try + { + webResponse = (HttpWebResponse)request.GetResponse(); + if (webResponse == null) + { + m_log.Debug("[REMOTE SIMULATION CONNECTOR]: Null reply on agent query "); + } + + sr = new StreamReader(webResponse.GetResponseStream()); + reply = sr.ReadToEnd().Trim(); + + + } + catch (WebException ex) + { + m_log.WarnFormat("[REMOTE SIMULATION CONNECTOR]: exception on reply of agent query {0}", ex.Message); + // ignore, really + return false; + } + finally + { + if (sr != null) + sr.Close(); + } + + if (webResponse.StatusCode == HttpStatusCode.OK) + { + try + { + bool result; + + result = bool.Parse(reply); + + return result; + } + catch + { + return false; + } + } + + return false; + } + public bool ReleaseAgent(UUID origin, UUID id, string uri) { WebRequest request = WebRequest.Create(uri); diff --git a/OpenSim/Services/Interfaces/ISimulationService.cs b/OpenSim/Services/Interfaces/ISimulationService.cs index 67d7cbe71b..12e898202e 100644 --- a/OpenSim/Services/Interfaces/ISimulationService.cs +++ b/OpenSim/Services/Interfaces/ISimulationService.cs @@ -60,6 +60,8 @@ namespace OpenSim.Services.Interfaces bool RetrieveAgent(GridRegion destination, UUID id, out IAgentData agent); + bool QueryAccess(GridRegion destination, UUID id); + /// /// Message from receiving region to departing region, telling it got contacted by the client. /// When sent over REST, it invokes the opaque uri. From 1070cffcf905f77d694e140d0c9e978f5d0052c0 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Wed, 8 Dec 2010 18:53:15 -0800 Subject: [PATCH 6/6] Added ability for GetTexture to serve multiple formats. The format may come as an extra query parameter in the URL format= (this was tested and working) or it may come in the Accept header (code added, but not tested). The result of the conversion is placed in the asset cache, under the name -. --- OpenSim/Framework/WebUtil.cs | 81 ++++++ .../Avatar/Assets/GetTextureModule.cs | 256 ++++++++++++++---- 2 files changed, 289 insertions(+), 48 deletions(-) diff --git a/OpenSim/Framework/WebUtil.cs b/OpenSim/Framework/WebUtil.cs index d16f9bf213..1c856af89e 100644 --- a/OpenSim/Framework/WebUtil.cs +++ b/OpenSim/Framework/WebUtil.cs @@ -26,6 +26,7 @@ */ using System; +using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.IO; @@ -363,5 +364,85 @@ namespace OpenSim.Framework } #endregion Stream + + public class QBasedComparer : IComparer + { + public int Compare(Object x, Object y) + { + float qx = GetQ(x); + float qy = GetQ(y); + if (qx < qy) + return -1; + if (qx == qy) + return 0; + return 1; + } + + private float GetQ(Object o) + { + // Example: image/png;q=0.9 + + if (o is String) + { + string mime = (string)o; + string[] parts = mime.Split(new char[] { ';' }); + if (parts.Length > 1) + { + string[] kvp = parts[1].Split(new char[] { '=' }); + if (kvp.Length == 2 && kvp[0] == "q") + { + float qvalue = 1F; + float.TryParse(kvp[1], out qvalue); + return qvalue; + } + } + } + + return 1F; + } + } + + /// + /// Takes the value of an Accept header and returns the preferred types + /// ordered by q value (if it exists). + /// Example input: image/jpg;q=0.7, image/png;q=0.8, image/jp2 + /// Exmaple output: ["jp2", "png", "jpg"] + /// NOTE: This doesn't handle the semantics of *'s... + /// + /// + /// + public static string[] GetPreferredImageTypes(string accept) + { + + if (accept == null || accept == string.Empty) + return new string[0]; + + string[] types = accept.Split(new char[] { ',' }); + if (types.Length > 0) + { + List list = new List(types); + list.RemoveAll(delegate(string s) { return !s.ToLower().StartsWith("image"); }); + ArrayList tlist = new ArrayList(list); + tlist.Sort(new QBasedComparer()); + + string[] result = new string[tlist.Count]; + for (int i = 0; i < tlist.Count; i++) + { + string mime = (string)tlist[i]; + string[] parts = mime.Split(new char[] { ';' }); + string[] pair = parts[0].Split(new char[] { '/' }); + if (pair.Length == 2) + result[i] = pair[1].ToLower(); + else // oops, we don't know what this is... + result[i] = pair[0]; + } + + return result; + } + + return new string[0]; + } + + } } diff --git a/OpenSim/Region/CoreModules/Avatar/Assets/GetTextureModule.cs b/OpenSim/Region/CoreModules/Avatar/Assets/GetTextureModule.cs index 97581e5206..ac2ad7af65 100644 --- a/OpenSim/Region/CoreModules/Avatar/Assets/GetTextureModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Assets/GetTextureModule.cs @@ -28,6 +28,8 @@ using System; using System.Collections; using System.Collections.Specialized; +using System.Drawing; +using System.Drawing.Imaging; using System.Reflection; using System.IO; using System.Web; @@ -35,6 +37,7 @@ using log4net; using Nini.Config; using OpenMetaverse; using OpenMetaverse.StructuredData; +using OpenMetaverse.Imaging; using OpenSim.Framework; using OpenSim.Framework.Servers; using OpenSim.Framework.Servers.HttpServer; @@ -74,6 +77,12 @@ namespace OpenSim.Region.CoreModules.Avatar.ObjectCaps private Scene m_scene; private IAssetService m_assetService; + public const string DefaultFormat = "x-j2c"; + + // TODO: Change this to a config option + const string REDIRECT_URL = null; + + #region IRegionModule Members public void Initialise(Scene pScene, IConfigSource pSource) @@ -96,7 +105,7 @@ namespace OpenSim.Region.CoreModules.Avatar.ObjectCaps { UUID capID = UUID.Random(); - m_log.Info("[GETTEXTURE]: /CAPS/" + capID); + m_log.InfoFormat("[GETTEXTURE]: /CAPS/{0} in region {1}", capID, m_scene.RegionInfo.RegionName); caps.RegisterHandler("GetTexture", new StreamHandler("GET", "/CAPS/" + capID, ProcessGetTexture)); } @@ -104,12 +113,12 @@ namespace OpenSim.Region.CoreModules.Avatar.ObjectCaps private byte[] ProcessGetTexture(string path, Stream request, OSHttpRequest httpRequest, OSHttpResponse httpResponse) { - // TODO: Change this to a config option - const string REDIRECT_URL = null; + //m_log.DebugFormat("[GETTEXTURE]: called in {0}", m_scene.RegionInfo.RegionName); // Try to parse the texture ID from the request URL NameValueCollection query = HttpUtility.ParseQueryString(httpRequest.Url.Query); string textureStr = query.GetOne("texture_id"); + string format = query.GetOne("format"); if (m_assetService == null) { @@ -121,52 +130,27 @@ namespace OpenSim.Region.CoreModules.Avatar.ObjectCaps UUID textureID; if (!String.IsNullOrEmpty(textureStr) && UUID.TryParse(textureStr, out textureID)) { - //m_log.DebugFormat("[GETTEXTURE]: {0}", textureID); - AssetBase texture; - - if (!String.IsNullOrEmpty(REDIRECT_URL)) + string[] formats; + if (format != null && format != string.Empty) { - // Only try to fetch locally cached textures. Misses are redirected - texture = m_assetService.GetCached(textureID.ToString()); - - if (texture != null) - { - if (texture.Type != (sbyte)AssetType.Texture) - { - httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound; - httpResponse.Send(); - return null; - } - SendTexture(httpRequest, httpResponse, texture); - } - else - { - string textureUrl = REDIRECT_URL + textureID.ToString(); - m_log.Debug("[GETTEXTURE]: Redirecting texture request to " + textureUrl); - httpResponse.RedirectLocation = textureUrl; - } + formats = new string[1] { format.ToLower() }; } else { - // Fetch locally or remotely. Misses return a 404 - texture = m_assetService.Get(textureID.ToString()); + formats = WebUtil.GetPreferredImageTypes(httpRequest.Headers.Get("Accept")); + if (formats.Length == 0) + formats = new string[1] { DefaultFormat }; // default - if (texture != null) - { - if (texture.Type != (sbyte)AssetType.Texture) - { - httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound; - httpResponse.Send(); - return null; - } - SendTexture(httpRequest, httpResponse, texture); - } - else - { - m_log.Warn("[GETTEXTURE]: Texture " + textureID + " not found"); - httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound; - } } + // OK, we have an array with preferred formats, possibly with only one entry + + httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound; + foreach (string f in formats) + { + if (FetchTexture(httpRequest, httpResponse, textureID, f)) + break; + } + } else { @@ -177,11 +161,105 @@ namespace OpenSim.Region.CoreModules.Avatar.ObjectCaps return null; } - private void SendTexture(OSHttpRequest request, OSHttpResponse response, AssetBase texture) + /// + /// + /// + /// + /// + /// + /// + /// False for "caller try another codec"; true otherwise + private bool FetchTexture(OSHttpRequest httpRequest, OSHttpResponse httpResponse, UUID textureID, string format) + { + m_log.DebugFormat("[GETTEXTURE]: {0} with requested format {1}", textureID, format); + AssetBase texture; + + string fullID = textureID.ToString(); + if (format != DefaultFormat) + fullID = fullID + "-" + format; + + if (!String.IsNullOrEmpty(REDIRECT_URL)) + { + // Only try to fetch locally cached textures. Misses are redirected + texture = m_assetService.GetCached(fullID); + + if (texture != null) + { + if (texture.Type != (sbyte)AssetType.Texture) + { + httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound; + return true; + } + WriteTextureData(httpRequest, httpResponse, texture, format); + } + else + { + string textureUrl = REDIRECT_URL + textureID.ToString(); + m_log.Debug("[GETTEXTURE]: Redirecting texture request to " + textureUrl); + httpResponse.RedirectLocation = textureUrl; + return true; + } + } + else // no redirect + { + // try the cache + texture = m_assetService.GetCached(fullID); + + if (texture == null) + { + //m_log.DebugFormat("[GETTEXTURE]: texture was not in the cache"); + + // Fetch locally or remotely. Misses return a 404 + texture = m_assetService.Get(textureID.ToString()); + + if (texture != null) + { + if (texture.Type != (sbyte)AssetType.Texture) + { + httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound; + return true; + } + if (format == DefaultFormat) + { + WriteTextureData(httpRequest, httpResponse, texture, format); + return true; + } + else + { + AssetBase newTexture = new AssetBase(texture.ID + "-" + format, texture.Name, (sbyte)AssetType.Texture, texture.CreatorID); + newTexture.Data = ConvertTextureData(texture, format); + if (newTexture.Data.Length == 0) + return false; // !!! Caller try another codec, please! + + newTexture.Flags = AssetFlags.Collectable; + newTexture.Temporary = true; + m_assetService.Store(newTexture); + WriteTextureData(httpRequest, httpResponse, newTexture, format); + return true; + } + } + } + else // it was on the cache + { + //m_log.DebugFormat("[GETTEXTURE]: texture was in the cache"); + WriteTextureData(httpRequest, httpResponse, texture, format); + return true; + } + + } + + // not found + m_log.Warn("[GETTEXTURE]: Texture " + textureID + " not found"); + httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound; + return true; + + } + + private void WriteTextureData(OSHttpRequest request, OSHttpResponse response, AssetBase texture, string format) { string range = request.Headers.GetOne("Range"); //m_log.DebugFormat("[GETTEXTURE]: Range {0}", range); - if (!String.IsNullOrEmpty(range)) + if (!String.IsNullOrEmpty(range)) // JP2's only { // Range request int start, end; @@ -212,15 +290,19 @@ namespace OpenSim.Region.CoreModules.Avatar.ObjectCaps } else { - m_log.Warn("Malformed Range header: " + range); + m_log.Warn("[GETTEXTURE]: Malformed Range header: " + range); response.StatusCode = (int)System.Net.HttpStatusCode.BadRequest; } } - else + else // JP2's or other formats { // Full content request + response.StatusCode = (int)System.Net.HttpStatusCode.OK; response.ContentLength = texture.Data.Length; - response.ContentType = texture.Metadata.ContentType; + if (format == DefaultFormat) + response.ContentType = texture.Metadata.ContentType; + else + response.ContentType = "image/" + format; response.Body.Write(texture.Data, 0, texture.Data.Length); } } @@ -240,5 +322,83 @@ namespace OpenSim.Region.CoreModules.Avatar.ObjectCaps start = end = 0; return false; } + + + private byte[] ConvertTextureData(AssetBase texture, string format) + { + m_log.DebugFormat("[GETTEXTURE]: Converting texture {0} to {1}", texture.ID, format); + byte[] data = new byte[0]; + + MemoryStream imgstream = new MemoryStream(); + Bitmap mTexture = new Bitmap(1, 1); + ManagedImage managedImage; + Image image = (Image)mTexture; + + try + { + // Taking our jpeg2000 data, decoding it, then saving it to a byte array with regular jpeg data + + imgstream = new MemoryStream(); + + // Decode image to System.Drawing.Image + if (OpenJPEG.DecodeToImage(texture.Data, out managedImage, out image)) + { + // Save to bitmap + mTexture = new Bitmap(image); + + EncoderParameters myEncoderParameters = new EncoderParameters(); + myEncoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, 95L); + + // Save bitmap to stream + ImageCodecInfo codec = GetEncoderInfo("image/" + format); + if (codec != null) + { + mTexture.Save(imgstream, codec, myEncoderParameters); + // Write the stream to a byte array for output + data = imgstream.ToArray(); + } + else + m_log.WarnFormat("[GETTEXTURE]: No such codec {0}", format); + + } + } + catch (Exception e) + { + m_log.WarnFormat("[GETTEXTURE]: Unable to convert texture {0} to {1}: {2}", texture.ID, format, e.Message); + } + finally + { + // Reclaim memory, these are unmanaged resources + // If we encountered an exception, one or more of these will be null + if (mTexture != null) + mTexture.Dispose(); + + if (image != null) + image.Dispose(); + + if (imgstream != null) + { + imgstream.Close(); + imgstream.Dispose(); + } + } + + return data; + } + + // From msdn + private static ImageCodecInfo GetEncoderInfo(String mimeType) + { + ImageCodecInfo[] encoders; + encoders = ImageCodecInfo.GetImageEncoders(); + for (int j = 0; j < encoders.Length; ++j) + { + if (encoders[j].MimeType == mimeType) + return encoders[j]; + } + return null; + } + + } }