From 142b481e032f7b8206a19e78af82847370b8bf9c Mon Sep 17 00:00:00 2001 From: Jeff Ames Date: Mon, 22 Jun 2009 10:07:27 +0000 Subject: Update svn properties. --- OpenSim/Framework/Capabilities/Caps.cs | 2424 ++++++++++++++++---------------- 1 file changed, 1212 insertions(+), 1212 deletions(-) (limited to 'OpenSim/Framework/Capabilities/Caps.cs') diff --git a/OpenSim/Framework/Capabilities/Caps.cs b/OpenSim/Framework/Capabilities/Caps.cs index 12bf04d..df29c4e 100644 --- a/OpenSim/Framework/Capabilities/Caps.cs +++ b/OpenSim/Framework/Capabilities/Caps.cs @@ -1,1212 +1,1212 @@ -/* - * 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.IO; -using System.Reflection; -using log4net; -using OpenMetaverse; -using OpenSim.Framework.Servers; -using OpenSim.Framework.Servers.HttpServer; -using OpenSim.Services.Interfaces; - -// using OpenSim.Region.Framework.Interfaces; - -namespace OpenSim.Framework.Capabilities -{ - public delegate void UpLoadedAsset( - string assetName, string description, UUID assetID, UUID inventoryItem, UUID parentFolder, - byte[] data, string inventoryType, string assetType); - - public delegate UUID UpdateItem(UUID itemID, byte[] data); - - public delegate void UpdateTaskScript(UUID itemID, UUID primID, bool isScriptRunning, byte[] data); - - public delegate void NewInventoryItem(UUID userID, InventoryItemBase item); - - public delegate void NewAsset(AssetBase asset); - - public delegate UUID ItemUpdatedCallback(UUID userID, UUID itemID, byte[] data); - - public delegate void TaskScriptUpdatedCallback(UUID userID, UUID itemID, UUID primID, - bool isScriptRunning, byte[] data); - - public delegate List FetchInventoryDescendentsCAPS(UUID agentID, UUID folderID, UUID ownerID, - bool fetchFolders, bool fetchItems, int sortOrder); - - /// - /// XXX Probably not a particularly nice way of allow us to get the scene presence from the scene (chiefly so that - /// we can popup a message on the user's client if the inventory service has permanently failed). But I didn't want - /// to just pass the whole Scene into CAPS. - /// - public delegate IClientAPI GetClientDelegate(UUID agentID); - - public class Caps - { - private static readonly ILog m_log = - LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - - private string m_httpListenerHostName; - private uint m_httpListenPort; - - /// - /// This is the uuid portion of every CAPS path. It is used to make capability urls private to the requester. - /// - private string m_capsObjectPath; - public string CapsObjectPath { get { return m_capsObjectPath; } } - - private CapsHandlers m_capsHandlers; - - private static readonly string m_requestPath = "0000/"; - // private static readonly string m_mapLayerPath = "0001/"; - private static readonly string m_newInventory = "0002/"; - //private static readonly string m_requestTexture = "0003/"; - private static readonly string m_notecardUpdatePath = "0004/"; - private static readonly string m_notecardTaskUpdatePath = "0005/"; - // private static readonly string m_fetchInventoryPath = "0006/"; - - // The following entries are in a module, however, they are also here so that we don't re-assign - // the path to another cap by mistake. - // private static readonly string m_parcelVoiceInfoRequestPath = "0007/"; // This is in a module. - // private static readonly string m_provisionVoiceAccountRequestPath = "0008/";// This is in a module. - - // private static readonly string m_remoteParcelRequestPath = "0009/";// This is in the LandManagementModule. - - //private string eventQueue = "0100/"; - private IHttpServer m_httpListener; - private UUID m_agentID; - private IAssetService m_assetCache; - private int m_eventQueueCount = 1; - private Queue m_capsEventQueue = new Queue(); - private bool m_dumpAssetsToFile; - private string m_regionName; - - public bool SSLCaps - { - get { return m_httpListener.UseSSL; } - } - public string SSLCommonName - { - get { return m_httpListener.SSLCommonName; } - } - public CapsHandlers CapsHandlers - { - get { return m_capsHandlers; } - } - - // These are callbacks which will be setup by the scene so that we can update scene data when we - // receive capability calls - public NewInventoryItem AddNewInventoryItem = null; - public NewAsset AddNewAsset = null; - public ItemUpdatedCallback ItemUpdatedCall = null; - public TaskScriptUpdatedCallback TaskScriptUpdatedCall = null; - public FetchInventoryDescendentsCAPS CAPSFetchInventoryDescendents = null; - public GetClientDelegate GetClient = null; - - public Caps(IAssetService assetCache, IHttpServer httpServer, string httpListen, uint httpPort, string capsPath, - UUID agent, bool dumpAssetsToFile, string regionName) - { - m_assetCache = assetCache; - m_capsObjectPath = capsPath; - m_httpListener = httpServer; - m_httpListenerHostName = httpListen; - - m_httpListenPort = httpPort; - - if (httpServer.UseSSL) - { - m_httpListenPort = httpServer.SSLPort; - httpListen = httpServer.SSLCommonName; - httpPort = httpServer.SSLPort; - } - - m_agentID = agent; - m_dumpAssetsToFile = dumpAssetsToFile; - m_capsHandlers = new CapsHandlers(httpServer, httpListen, httpPort, httpServer.UseSSL); - m_regionName = regionName; - } - - /// - /// Register all CAPS http service handlers - /// - public void RegisterHandlers() - { - DeregisterHandlers(); - - string capsBase = "/CAPS/" + m_capsObjectPath; - - RegisterRegionServiceHandlers(capsBase); - RegisterInventoryServiceHandlers(capsBase); - - } - - public void RegisterRegionServiceHandlers(string capsBase) - { - try - { - // the root of all evil - m_capsHandlers["SEED"] = new RestStreamHandler("POST", capsBase + m_requestPath, CapsRequest); - m_log.DebugFormat( - "[CAPS]: Registered seed capability {0} for {1}", capsBase + m_requestPath, m_agentID); - - //m_capsHandlers["MapLayer"] = - // new LLSDStreamhandler("POST", - // capsBase + m_mapLayerPath, - // GetMapLayer); - m_capsHandlers["UpdateScriptTaskInventory"] = - new RestStreamHandler("POST", capsBase + m_notecardTaskUpdatePath, ScriptTaskInventory); - m_capsHandlers["UpdateScriptTask"] = m_capsHandlers["UpdateScriptTaskInventory"]; - - } - catch (Exception e) - { - m_log.Error("[CAPS]: " + e.ToString()); - } - } - - public void RegisterInventoryServiceHandlers(string capsBase) - { - try - { - // I don't think this one works... - m_capsHandlers["NewFileAgentInventory"] = - new LLSDStreamhandler("POST", - capsBase + m_newInventory, - NewAgentInventoryRequest); - m_capsHandlers["UpdateNotecardAgentInventory"] = - new RestStreamHandler("POST", capsBase + m_notecardUpdatePath, NoteCardAgentInventory); - m_capsHandlers["UpdateScriptAgentInventory"] = m_capsHandlers["UpdateNotecardAgentInventory"]; - m_capsHandlers["UpdateScriptAgent"] = m_capsHandlers["UpdateScriptAgentInventory"]; - - // As of RC 1.22.9 of the Linden client this is - // supported - - // m_capsHandlers["WebFetchInventoryDescendents"] =new RestStreamHandler("POST", capsBase + m_fetchInventoryPath, FetchInventoryDescendentsRequest); - - // justincc: I've disabled the CAPS service for now to fix problems with selecting textures, and - // subsequent inventory breakage, in the edit object pane (such as mantis 1085). This requires - // enhancements (probably filling out the folder part of the LLSD reply) to our CAPS service, - // but when I went on the Linden grid, the - // simulators I visited (version 1.21) were, surprisingly, no longer supplying this capability. Instead, - // the 1.19.1.4 client appeared to be happily flowing inventory data over UDP - // - // This is very probably just a temporary measure - once the CAPS service appears again on the Linden grid - // we will be - // able to get the data we need to implement the necessary part of the protocol to fix the issue above. - // m_capsHandlers["FetchInventoryDescendents"] = - // new RestStreamHandler("POST", capsBase + m_fetchInventoryPath, FetchInventoryRequest); - - // m_capsHandlers["FetchInventoryDescendents"] = - // new LLSDStreamhandler("POST", - // capsBase + m_fetchInventory, - // FetchInventory)); - // m_capsHandlers["RequestTextureDownload"] = new RestStreamHandler("POST", - // capsBase + m_requestTexture, - // RequestTexture); - } - catch (Exception e) - { - m_log.Error("[CAPS]: " + e.ToString()); - } - } - - /// - /// Register a handler. This allows modules to register handlers. - /// - /// - /// - public void RegisterHandler(string capName, IRequestHandler handler) - { - m_capsHandlers[capName] = handler; - //m_log.DebugFormat("[CAPS]: Registering handler for \"{0}\": path {1}", capName, handler.Path); - } - - /// - /// Remove all CAPS service handlers. - /// - /// - /// - /// - /// - public void DeregisterHandlers() - { - if (m_capsHandlers != null) - { - foreach (string capsName in m_capsHandlers.Caps) - { - m_capsHandlers.Remove(capsName); - } - } - } - - /// - /// Construct a client response detailing all the capabilities this server can provide. - /// - /// - /// - /// - /// HTTP request header object - /// HTTP response header object - /// - public string CapsRequest(string request, string path, string param, - OSHttpRequest httpRequest, OSHttpResponse httpResponse) - { - //m_log.Debug("[CAPS]: Seed Caps Request in region: " + m_regionName); - - string result = LLSDHelpers.SerialiseLLSDReply(m_capsHandlers.CapsDetails); - - //m_log.DebugFormat("[CAPS] CapsRequest {0}", result); - - return result; - } - - // FIXME: these all should probably go into the respective region - // modules - - /// - /// Processes a fetch inventory request and sends the reply - - /// - /// - /// - /// - /// - // Request is like: - // - // folders - // - // - // fetch-folders1fetch-items1folder-id8e1e3a30-b9bf-11dc-95ff-0800200c9a66owner-id11111111-1111-0000-0000-000100bba000sort-order1 - // - // - // - // - // - // multiple fetch-folder maps are allowed within the larger folders map. - public string FetchInventoryRequest(string request, string path, string param) - { - // string unmodifiedRequest = request.ToString(); - - //m_log.DebugFormat("[AGENT INVENTORY]: Received CAPS fetch inventory request {0}", unmodifiedRequest); - m_log.Debug("[CAPS]: Inventory Request in region: " + m_regionName); - - Hashtable hash = new Hashtable(); - try - { - hash = (Hashtable)LLSD.LLSDDeserialize(Utils.StringToBytes(request)); - } - catch (LLSD.LLSDParseException pe) - { - m_log.Error("[AGENT INVENTORY]: Fetch error: " + pe.Message); - m_log.Error("Request: " + request.ToString()); - } - - ArrayList foldersrequested = (ArrayList)hash["folders"]; - - string response = ""; - - for (int i = 0; i < foldersrequested.Count; i++) - { - string inventoryitemstr = ""; - Hashtable inventoryhash = (Hashtable)foldersrequested[i]; - - LLSDFetchInventoryDescendents llsdRequest = new LLSDFetchInventoryDescendents(); - LLSDHelpers.DeserialiseOSDMap(inventoryhash, llsdRequest); - LLSDInventoryDescendents reply = FetchInventoryReply(llsdRequest); - - inventoryitemstr = LLSDHelpers.SerialiseLLSDReply(reply); - inventoryitemstr = inventoryitemstr.Replace("folders", ""); - inventoryitemstr = inventoryitemstr.Replace("", ""); - - response += inventoryitemstr; - } - - if (response.Length == 0) - { - // Ter-guess: If requests fail a lot, the client seems to stop requesting descendants. - // Therefore, I'm concluding that the client only has so many threads available to do requests - // and when a thread stalls.. is stays stalled. - // Therefore we need to return something valid - response = "folders"; - } - else - { - response = "folders" + response + ""; - } - - //m_log.DebugFormat("[AGENT INVENTORY]: Replying to CAPS fetch inventory request with following xml"); - //m_log.Debug(Util.GetFormattedXml(response)); - - return response; - } - - public string FetchInventoryDescendentsRequest(string request, string path, string param,OSHttpRequest httpRequest, OSHttpResponse httpResponse) - { - // m_log.Debug("[CAPS]: FetchInventoryDescendentsRequest in region: " + m_regionName + "request is "+request); - - // nasty temporary hack here, the linden client falsely identifies the uuid 00000000-0000-0000-0000-000000000000 as a string which breaks us - // correctly mark it as a uuid - request = request.Replace("00000000-0000-0000-0000-000000000000", "00000000-0000-0000-0000-000000000000"); - - // another hack 1 results in a System.ArgumentException: Object type System.Int32 cannot be converted to target type: System.Boolean - request = request.Replace("fetch_folders0", "fetch_folders0"); - request = request.Replace("fetch_folders1", "fetch_folders1"); - Hashtable hash = new Hashtable(); - try - { - hash = (Hashtable)LLSD.LLSDDeserialize(Utils.StringToBytes(request)); - } - catch (LLSD.LLSDParseException pe) - { - m_log.Error("[AGENT INVENTORY]: Fetch error: " + pe.Message); - m_log.Error("Request: " + request.ToString()); - } - - ArrayList foldersrequested = (ArrayList)hash["folders"]; - - string response = ""; - for (int i = 0; i < foldersrequested.Count; i++) - { - string inventoryitemstr = ""; - Hashtable inventoryhash = (Hashtable)foldersrequested[i]; - - LLSDFetchInventoryDescendents llsdRequest = new LLSDFetchInventoryDescendents(); - - try{ - LLSDHelpers.DeserialiseOSDMap(inventoryhash, llsdRequest); - } - catch(Exception e) - { - m_log.Debug("[CAPS]: caught exception doing OSD deserialize" + e); - } - LLSDInventoryDescendents reply = FetchInventoryReply(llsdRequest); - - inventoryitemstr = LLSDHelpers.SerialiseLLSDReply(reply); - inventoryitemstr = inventoryitemstr.Replace("folders", ""); - inventoryitemstr = inventoryitemstr.Replace("", ""); - - response += inventoryitemstr; - } - - - if (response.Length == 0) - { - // Ter-guess: If requests fail a lot, the client seems to stop requesting descendants. - // Therefore, I'm concluding that the client only has so many threads available to do requests - // and when a thread stalls.. is stays stalled. - // Therefore we need to return something valid - response = "folders"; - } - else - { - response = "folders" + response + ""; - } - - //m_log.DebugFormat("[CAPS]: Replying to CAPS fetch inventory request with following xml"); - //m_log.Debug("[CAPS] "+response); - - return response; - } - - - - /// - /// Construct an LLSD reply packet to a CAPS inventory request - /// - /// - /// - private LLSDInventoryDescendents FetchInventoryReply(LLSDFetchInventoryDescendents invFetch) - { - LLSDInventoryDescendents reply = new LLSDInventoryDescendents(); - LLSDInventoryFolderContents contents = new LLSDInventoryFolderContents(); - contents.agent_id = m_agentID; - contents.owner_id = invFetch.owner_id; - contents.folder_id = invFetch.folder_id; - - // The version number being sent back was originally 1. - // Unfortunately, on 1.19.1.4, this means that we see a problem where on subsequent logins - // without clearing client cache, objects in the root folder disappear until the cache is cleared, - // at which point they reappear. - // - // Seeing the version to something other than 0 may be the right thing to do, but there is - // a greater subtlety of the second life protocol that needs to be understood first. - contents.version = 0; - - contents.descendents = 0; - reply.folders.Array.Add(contents); - List itemList = null; - if (CAPSFetchInventoryDescendents != null) - { - itemList = CAPSFetchInventoryDescendents(m_agentID, invFetch.folder_id, invFetch.owner_id, invFetch.fetch_folders, invFetch.fetch_items, invFetch.sort_order); - } - - if (itemList != null) - { - foreach (InventoryItemBase invItem in itemList) - { - contents.items.Array.Add(ConvertInventoryItem(invItem)); - } - } - /* The following block is removed as it ALWAYS sends the error to the client because the RC 1.22.9 client tries to - find items that have become dissasociated with a paret folder and have parent of 00000000-0000-00000.... - else - { - IClientAPI client = GetClient(m_agentID); - - // We're going to both notify the client of inventory service failure and send back a 'no folder contents' response. - // If we don't send back the response, - // the client becomes unhappy (see Teravus' comment in FetchInventoryRequest()) - if (client != null) - { - client.SendAgentAlertMessage( - "AGIN0001E: The inventory service has either failed or is not responding. Your inventory will not function properly for the rest of this session. Please clear your cache and relog.", - true); - } - else - { - m_log.ErrorFormat( - "[AGENT INVENTORY]: Could not lookup controlling client for {0} in order to notify them of the inventory service failure", - m_agentID); - } - }*/ - - contents.descendents = contents.items.Array.Count; - return reply; - } - - /// - /// Convert an internal inventory item object into an LLSD object. - /// - /// - /// - private LLSDInventoryItem ConvertInventoryItem(InventoryItemBase invItem) - { - LLSDInventoryItem llsdItem = new LLSDInventoryItem(); - llsdItem.asset_id = invItem.AssetID; - llsdItem.created_at = invItem.CreationDate; - llsdItem.desc = invItem.Description; - llsdItem.flags = 0; - llsdItem.item_id = invItem.ID; - llsdItem.name = invItem.Name; - llsdItem.parent_id = invItem.Folder; - try - { - // TODO reevaluate after upgrade to libomv >= r2566. Probably should use UtilsConversions. - llsdItem.type = TaskInventoryItem.Types[invItem.AssetType]; - llsdItem.inv_type = TaskInventoryItem.InvTypes[invItem.InvType]; - } - catch (Exception e) - { - m_log.Error("[CAPS]: Problem setting asset/inventory type while converting inventory item " + invItem.Name + " to LLSD:", e); - } - llsdItem.permissions = new LLSDPermissions(); - llsdItem.permissions.creator_id = invItem.CreatorIdAsUuid; - llsdItem.permissions.base_mask = (int)invItem.CurrentPermissions; - llsdItem.permissions.everyone_mask = (int)invItem.EveryOnePermissions; - llsdItem.permissions.group_id = UUID.Zero; - llsdItem.permissions.group_mask = 0; - llsdItem.permissions.is_owner_group = false; - llsdItem.permissions.next_owner_mask = (int)invItem.NextPermissions; - llsdItem.permissions.owner_id = m_agentID; // FixMe - llsdItem.permissions.owner_mask = (int)invItem.CurrentPermissions; - llsdItem.sale_info = new LLSDSaleInfo(); - llsdItem.sale_info.sale_price = 10; - llsdItem.sale_info.sale_type = "not"; - - return llsdItem; - } - - /// - /// - /// - /// - /// - public LLSDMapLayerResponse GetMapLayer(LLSDMapRequest mapReq) - { - m_log.Debug("[CAPS]: MapLayer Request in region: " + m_regionName); - LLSDMapLayerResponse mapResponse = new LLSDMapLayerResponse(); - mapResponse.LayerData.Array.Add(GetOSDMapLayerResponse()); - return mapResponse; - } - - /// - /// - /// - /// - protected static OSDMapLayer GetOSDMapLayerResponse() - { - OSDMapLayer mapLayer = new OSDMapLayer(); - mapLayer.Right = 5000; - mapLayer.Top = 5000; - mapLayer.ImageID = new UUID("00000000-0000-1111-9999-000000000006"); - - return mapLayer; - } - - /// - /// - /// - /// - /// - /// - /// - public string RequestTexture(string request, string path, string param) - { - m_log.Debug("texture request " + request); - // Needs implementing (added to remove compiler warning) - return String.Empty; - } - - #region EventQueue (Currently not enabled) - - /// - /// - /// - /// - /// - /// - /// - public string ProcessEventQueue(string request, string path, string param) - { - string res = String.Empty; - - if (m_capsEventQueue.Count > 0) - { - lock (m_capsEventQueue) - { - string item = m_capsEventQueue.Dequeue(); - res = item; - } - } - else - { - res = CreateEmptyEventResponse(); - } - return res; - } - - /// - /// - /// - /// - /// - /// - public string CreateEstablishAgentComms(string caps, string ipAddressPort) - { - LLSDCapEvent eventItem = new LLSDCapEvent(); - eventItem.id = m_eventQueueCount; - //should be creating a EstablishAgentComms item, but there isn't a class for it yet - eventItem.events.Array.Add(new LLSDEmpty()); - string res = LLSDHelpers.SerialiseLLSDReply(eventItem); - m_eventQueueCount++; - - m_capsEventQueue.Enqueue(res); - return res; - } - - /// - /// - /// - /// - public string CreateEmptyEventResponse() - { - LLSDCapEvent eventItem = new LLSDCapEvent(); - eventItem.id = m_eventQueueCount; - eventItem.events.Array.Add(new LLSDEmpty()); - string res = LLSDHelpers.SerialiseLLSDReply(eventItem); - m_eventQueueCount++; - return res; - } - - #endregion - - /// - /// Called by the script task update handler. Provides a URL to which the client can upload a new asset. - /// - /// - /// - /// - /// HTTP request header object - /// HTTP response header object - /// - public string ScriptTaskInventory(string request, string path, string param, - OSHttpRequest httpRequest, OSHttpResponse httpResponse) - { - try - { - m_log.Debug("[CAPS]: ScriptTaskInventory Request in region: " + m_regionName); - //m_log.DebugFormat("[CAPS]: request: {0}, path: {1}, param: {2}", request, path, param); - - Hashtable hash = (Hashtable) LLSD.LLSDDeserialize(Utils.StringToBytes(request)); - LLSDTaskScriptUpdate llsdUpdateRequest = new LLSDTaskScriptUpdate(); - LLSDHelpers.DeserialiseOSDMap(hash, llsdUpdateRequest); - - string capsBase = "/CAPS/" + m_capsObjectPath; - string uploaderPath = Util.RandomClass.Next(5000, 8000).ToString("0000"); - - TaskInventoryScriptUpdater uploader = - new TaskInventoryScriptUpdater( - llsdUpdateRequest.item_id, - llsdUpdateRequest.task_id, - llsdUpdateRequest.is_script_running, - capsBase + uploaderPath, - m_httpListener, - m_dumpAssetsToFile); - uploader.OnUpLoad += TaskScriptUpdated; - - m_httpListener.AddStreamHandler( - new BinaryStreamHandler("POST", capsBase + uploaderPath, uploader.uploaderCaps)); - - string protocol = "http://"; - - if (m_httpListener.UseSSL) - protocol = "https://"; - - string uploaderURL = protocol + m_httpListenerHostName + ":" + m_httpListenPort.ToString() + capsBase + - uploaderPath; - - LLSDAssetUploadResponse uploadResponse = new LLSDAssetUploadResponse(); - uploadResponse.uploader = uploaderURL; - uploadResponse.state = "upload"; - -// m_log.InfoFormat("[CAPS]: " + -// "ScriptTaskInventory response: {0}", -// LLSDHelpers.SerialiseLLSDReply(uploadResponse))); - - return LLSDHelpers.SerialiseLLSDReply(uploadResponse); - } - catch (Exception e) - { - m_log.Error("[CAPS]: " + e.ToString()); - } - - return null; - } - - /// - /// Called by the notecard update handler. Provides a URL to which the client can upload a new asset. - /// - /// - /// - /// - /// - public string NoteCardAgentInventory(string request, string path, string param, - OSHttpRequest httpRequest, OSHttpResponse httpResponse) - { - //m_log.Debug("[CAPS]: NoteCardAgentInventory Request in region: " + m_regionName + "\n" + request); - //m_log.Debug("[CAPS]: NoteCardAgentInventory Request is: " + request); - - //OpenMetaverse.StructuredData.OSDMap hash = (OpenMetaverse.StructuredData.OSDMap)OpenMetaverse.StructuredData.LLSDParser.DeserializeBinary(Utils.StringToBytes(request)); - Hashtable hash = (Hashtable) LLSD.LLSDDeserialize(Utils.StringToBytes(request)); - LLSDItemUpdate llsdRequest = new LLSDItemUpdate(); - LLSDHelpers.DeserialiseOSDMap(hash, llsdRequest); - - string capsBase = "/CAPS/" + m_capsObjectPath; - string uploaderPath = Util.RandomClass.Next(5000, 8000).ToString("0000"); - - ItemUpdater uploader = - new ItemUpdater(llsdRequest.item_id, capsBase + uploaderPath, m_httpListener, m_dumpAssetsToFile); - uploader.OnUpLoad += ItemUpdated; - - m_httpListener.AddStreamHandler( - new BinaryStreamHandler("POST", capsBase + uploaderPath, uploader.uploaderCaps)); - - string protocol = "http://"; - - if (m_httpListener.UseSSL) - protocol = "https://"; - - string uploaderURL = protocol + m_httpListenerHostName + ":" + m_httpListenPort.ToString() + capsBase + - uploaderPath; - - LLSDAssetUploadResponse uploadResponse = new LLSDAssetUploadResponse(); - uploadResponse.uploader = uploaderURL; - uploadResponse.state = "upload"; - -// m_log.InfoFormat("[CAPS]: " + -// "NoteCardAgentInventory response: {0}", -// LLSDHelpers.SerialiseLLSDReply(uploadResponse))); - - return LLSDHelpers.SerialiseLLSDReply(uploadResponse); - } - - /// - /// - /// - /// - /// - public LLSDAssetUploadResponse NewAgentInventoryRequest(LLSDAssetUploadRequest llsdRequest) - { - //m_log.Debug("[CAPS]: NewAgentInventoryRequest Request is: " + llsdRequest.ToString()); - //m_log.Debug("asset upload request via CAPS" + llsdRequest.inventory_type + " , " + llsdRequest.asset_type); - - if (llsdRequest.asset_type == "texture" || - llsdRequest.asset_type == "animation" || - llsdRequest.asset_type == "sound") - { - IClientAPI client = null; - IScene scene = null; - if (GetClient != null) - { - client = GetClient(m_agentID); - scene = client.Scene; - - IMoneyModule mm = scene.RequestModuleInterface(); - - if (mm != null) - { - if (!mm.UploadCovered(client)) - { - if (client != null) - client.SendAgentAlertMessage("Unable to upload asset. Insufficient funds.", false); - - LLSDAssetUploadResponse errorResponse = new LLSDAssetUploadResponse(); - errorResponse.uploader = ""; - errorResponse.state = "error"; - return errorResponse; - } - } - } - } - - - string assetName = llsdRequest.name; - string assetDes = llsdRequest.description; - string capsBase = "/CAPS/" + m_capsObjectPath; - UUID newAsset = UUID.Random(); - UUID newInvItem = UUID.Random(); - UUID parentFolder = llsdRequest.folder_id; - string uploaderPath = Util.RandomClass.Next(5000, 8000).ToString("0000"); - - AssetUploader uploader = - new AssetUploader(assetName, assetDes, newAsset, newInvItem, parentFolder, llsdRequest.inventory_type, - llsdRequest.asset_type, capsBase + uploaderPath, m_httpListener, m_dumpAssetsToFile); - m_httpListener.AddStreamHandler( - new BinaryStreamHandler("POST", capsBase + uploaderPath, uploader.uploaderCaps)); - - string protocol = "http://"; - - if (m_httpListener.UseSSL) - protocol = "https://"; - - string uploaderURL = protocol + m_httpListenerHostName + ":" + m_httpListenPort.ToString() + capsBase + - uploaderPath; - - LLSDAssetUploadResponse uploadResponse = new LLSDAssetUploadResponse(); - uploadResponse.uploader = uploaderURL; - uploadResponse.state = "upload"; - uploader.OnUpLoad += UploadCompleteHandler; - return uploadResponse; - } - - /// - /// - /// - /// - /// - /// - public void UploadCompleteHandler(string assetName, string assetDescription, UUID assetID, - UUID inventoryItem, UUID parentFolder, byte[] data, string inventoryType, - string assetType) - { - sbyte assType = 0; - sbyte inType = 0; - - if (inventoryType == "sound") - { - inType = 1; - assType = 1; - } - else if (inventoryType == "animation") - { - inType = 19; - assType = 20; - } - else if (inventoryType == "wearable") - { - inType = 18; - switch (assetType) - { - case "bodypart": - assType = 13; - break; - case "clothing": - assType = 5; - break; - } - } - - AssetBase asset; - asset = new AssetBase(); - asset.FullID = assetID; - asset.Type = assType; - asset.Name = assetName; - asset.Data = data; - if (AddNewAsset != null) - AddNewAsset(asset); - else if (m_assetCache != null) - m_assetCache.Store(asset); - - InventoryItemBase item = new InventoryItemBase(); - item.Owner = m_agentID; - item.CreatorId = m_agentID.ToString(); - item.ID = inventoryItem; - item.AssetID = asset.FullID; - item.Description = assetDescription; - item.Name = assetName; - item.AssetType = assType; - item.InvType = inType; - item.Folder = parentFolder; - item.CurrentPermissions = 2147483647; - item.BasePermissions = 2147483647; - item.EveryOnePermissions = 0; - item.NextPermissions = 2147483647; - item.CreationDate = Util.UnixTimeSinceEpoch(); - - if (AddNewInventoryItem != null) - { - AddNewInventoryItem(m_agentID, item); - } - } - - /// - /// Called when new asset data for an agent inventory item update has been uploaded. - /// - /// Item to update - /// New asset data - /// - public UUID ItemUpdated(UUID itemID, byte[] data) - { - if (ItemUpdatedCall != null) - { - return ItemUpdatedCall(m_agentID, itemID, data); - } - - return UUID.Zero; - } - - /// - /// Called when new asset data for an agent inventory item update has been uploaded. - /// - /// Item to update - /// Prim containing item to update - /// Signals whether the script to update is currently running - /// New asset data - public void TaskScriptUpdated(UUID itemID, UUID primID, bool isScriptRunning, byte[] data) - { - if (TaskScriptUpdatedCall != null) - { - TaskScriptUpdatedCall(m_agentID, itemID, primID, isScriptRunning, data); - } - } - - public class AssetUploader - { - public event UpLoadedAsset OnUpLoad; - private UpLoadedAsset handlerUpLoad = null; - - private string uploaderPath = String.Empty; - private UUID newAssetID; - private UUID inventoryItemID; - private UUID parentFolder; - private IHttpServer httpListener; - private bool m_dumpAssetsToFile; - private string m_assetName = String.Empty; - private string m_assetDes = String.Empty; - - private string m_invType = String.Empty; - private string m_assetType = String.Empty; - - public AssetUploader(string assetName, string description, UUID assetID, UUID inventoryItem, - UUID parentFolderID, string invType, string assetType, string path, - IHttpServer httpServer, bool dumpAssetsToFile) - { - m_assetName = assetName; - m_assetDes = description; - newAssetID = assetID; - inventoryItemID = inventoryItem; - uploaderPath = path; - httpListener = httpServer; - parentFolder = parentFolderID; - m_assetType = assetType; - m_invType = invType; - m_dumpAssetsToFile = dumpAssetsToFile; - } - - /// - /// - /// - /// - /// - /// - /// - public string uploaderCaps(byte[] data, string path, string param) - { - UUID inv = inventoryItemID; - string res = String.Empty; - LLSDAssetUploadComplete uploadComplete = new LLSDAssetUploadComplete(); - uploadComplete.new_asset = newAssetID.ToString(); - uploadComplete.new_inventory_item = inv; - uploadComplete.state = "complete"; - - res = LLSDHelpers.SerialiseLLSDReply(uploadComplete); - - httpListener.RemoveStreamHandler("POST", uploaderPath); - - // TODO: probably make this a better set of extensions here - string extension = ".jp2"; - if (m_invType != "image") - { - extension = ".dat"; - } - - if (m_dumpAssetsToFile) - { - SaveAssetToFile(m_assetName + extension, data); - } - handlerUpLoad = OnUpLoad; - if (handlerUpLoad != null) - { - handlerUpLoad(m_assetName, m_assetDes, newAssetID, inv, parentFolder, data, m_invType, m_assetType); - } - - return res; - } - ///Left this in and commented in case there are unforseen issues - //private void SaveAssetToFile(string filename, byte[] data) - //{ - // FileStream fs = File.Create(filename); - // BinaryWriter bw = new BinaryWriter(fs); - // bw.Write(data); - // bw.Close(); - // fs.Close(); - //} - private static void SaveAssetToFile(string filename, byte[] data) - { - string assetPath = "UserAssets"; - if (!Directory.Exists(assetPath)) - { - Directory.CreateDirectory(assetPath); - } - FileStream fs = File.Create(Path.Combine(assetPath, Util.safeFileName(filename))); - BinaryWriter bw = new BinaryWriter(fs); - bw.Write(data); - bw.Close(); - fs.Close(); - } - } - - /// - /// This class is a callback invoked when a client sends asset data to - /// an agent inventory notecard update url - /// - public class ItemUpdater - { - public event UpdateItem OnUpLoad; - - private UpdateItem handlerUpdateItem = null; - - private string uploaderPath = String.Empty; - private UUID inventoryItemID; - private IHttpServer httpListener; - private bool m_dumpAssetToFile; - - public ItemUpdater(UUID inventoryItem, string path, IHttpServer httpServer, bool dumpAssetToFile) - { - m_dumpAssetToFile = dumpAssetToFile; - - inventoryItemID = inventoryItem; - uploaderPath = path; - httpListener = httpServer; - } - - /// - /// - /// - /// - /// - /// - /// - public string uploaderCaps(byte[] data, string path, string param) - { - UUID inv = inventoryItemID; - string res = String.Empty; - LLSDAssetUploadComplete uploadComplete = new LLSDAssetUploadComplete(); - UUID assetID = UUID.Zero; - handlerUpdateItem = OnUpLoad; - if (handlerUpdateItem != null) - { - assetID = handlerUpdateItem(inv, data); - } - - uploadComplete.new_asset = assetID.ToString(); - uploadComplete.new_inventory_item = inv; - uploadComplete.state = "complete"; - - res = LLSDHelpers.SerialiseLLSDReply(uploadComplete); - - httpListener.RemoveStreamHandler("POST", uploaderPath); - - if (m_dumpAssetToFile) - { - SaveAssetToFile("updateditem" + Util.RandomClass.Next(1, 1000) + ".dat", data); - } - - return res; - } - ///Left this in and commented in case there are unforseen issues - //private void SaveAssetToFile(string filename, byte[] data) - //{ - // FileStream fs = File.Create(filename); - // BinaryWriter bw = new BinaryWriter(fs); - // bw.Write(data); - // bw.Close(); - // fs.Close(); - //} - private static void SaveAssetToFile(string filename, byte[] data) - { - string assetPath = "UserAssets"; - if (!Directory.Exists(assetPath)) - { - Directory.CreateDirectory(assetPath); - } - FileStream fs = File.Create(Path.Combine(assetPath, filename)); - BinaryWriter bw = new BinaryWriter(fs); - bw.Write(data); - bw.Close(); - fs.Close(); - } - } - - /// - /// This class is a callback invoked when a client sends asset data to - /// a task inventory script update url - /// - public class TaskInventoryScriptUpdater - { - public event UpdateTaskScript OnUpLoad; - - private UpdateTaskScript handlerUpdateTaskScript = null; - - private string uploaderPath = String.Empty; - private UUID inventoryItemID; - private UUID primID; - private bool isScriptRunning; - private IHttpServer httpListener; - private bool m_dumpAssetToFile; - - public TaskInventoryScriptUpdater(UUID inventoryItemID, UUID primID, int isScriptRunning, - string path, IHttpServer httpServer, bool dumpAssetToFile) - { - m_dumpAssetToFile = dumpAssetToFile; - - this.inventoryItemID = inventoryItemID; - this.primID = primID; - - // This comes in over the packet as an integer, but actually appears to be treated as a bool - this.isScriptRunning = (0 == isScriptRunning ? false : true); - - uploaderPath = path; - httpListener = httpServer; - } - - /// - /// - /// - /// - /// - /// - /// - public string uploaderCaps(byte[] data, string path, string param) - { - try - { -// m_log.InfoFormat("[CAPS]: " + -// "TaskInventoryScriptUpdater received data: {0}, path: {1}, param: {2}", -// data, path, param)); - - string res = String.Empty; - LLSDTaskInventoryUploadComplete uploadComplete = new LLSDTaskInventoryUploadComplete(); - - handlerUpdateTaskScript = OnUpLoad; - if (handlerUpdateTaskScript != null) - { - handlerUpdateTaskScript(inventoryItemID, primID, isScriptRunning, data); - } - - uploadComplete.item_id = inventoryItemID; - uploadComplete.task_id = primID; - uploadComplete.state = "complete"; - - res = LLSDHelpers.SerialiseLLSDReply(uploadComplete); - - httpListener.RemoveStreamHandler("POST", uploaderPath); - - if (m_dumpAssetToFile) - { - SaveAssetToFile("updatedtaskscript" + Util.RandomClass.Next(1, 1000) + ".dat", data); - } - -// m_log.InfoFormat("[CAPS]: TaskInventoryScriptUpdater.uploaderCaps res: {0}", res); - - return res; - } - catch (Exception e) - { - m_log.Error("[CAPS]: " + e.ToString()); - } - - // XXX Maybe this should be some meaningful error packet - return null; - } - ///Left this in and commented in case there are unforseen issues - //private void SaveAssetToFile(string filename, byte[] data) - //{ - // FileStream fs = File.Create(filename); - // BinaryWriter bw = new BinaryWriter(fs); - // bw.Write(data); - // bw.Close(); - // fs.Close(); - //} - private static void SaveAssetToFile(string filename, byte[] data) - { - string assetPath = "UserAssets"; - if (!Directory.Exists(assetPath)) - { - Directory.CreateDirectory(assetPath); - } - FileStream fs = File.Create(Path.Combine(assetPath, filename)); - BinaryWriter bw = new BinaryWriter(fs); - bw.Write(data); - bw.Close(); - fs.Close(); - } - } - } -} +/* + * 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.IO; +using System.Reflection; +using log4net; +using OpenMetaverse; +using OpenSim.Framework.Servers; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Services.Interfaces; + +// using OpenSim.Region.Framework.Interfaces; + +namespace OpenSim.Framework.Capabilities +{ + public delegate void UpLoadedAsset( + string assetName, string description, UUID assetID, UUID inventoryItem, UUID parentFolder, + byte[] data, string inventoryType, string assetType); + + public delegate UUID UpdateItem(UUID itemID, byte[] data); + + public delegate void UpdateTaskScript(UUID itemID, UUID primID, bool isScriptRunning, byte[] data); + + public delegate void NewInventoryItem(UUID userID, InventoryItemBase item); + + public delegate void NewAsset(AssetBase asset); + + public delegate UUID ItemUpdatedCallback(UUID userID, UUID itemID, byte[] data); + + public delegate void TaskScriptUpdatedCallback(UUID userID, UUID itemID, UUID primID, + bool isScriptRunning, byte[] data); + + public delegate List FetchInventoryDescendentsCAPS(UUID agentID, UUID folderID, UUID ownerID, + bool fetchFolders, bool fetchItems, int sortOrder); + + /// + /// XXX Probably not a particularly nice way of allow us to get the scene presence from the scene (chiefly so that + /// we can popup a message on the user's client if the inventory service has permanently failed). But I didn't want + /// to just pass the whole Scene into CAPS. + /// + public delegate IClientAPI GetClientDelegate(UUID agentID); + + public class Caps + { + private static readonly ILog m_log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private string m_httpListenerHostName; + private uint m_httpListenPort; + + /// + /// This is the uuid portion of every CAPS path. It is used to make capability urls private to the requester. + /// + private string m_capsObjectPath; + public string CapsObjectPath { get { return m_capsObjectPath; } } + + private CapsHandlers m_capsHandlers; + + private static readonly string m_requestPath = "0000/"; + // private static readonly string m_mapLayerPath = "0001/"; + private static readonly string m_newInventory = "0002/"; + //private static readonly string m_requestTexture = "0003/"; + private static readonly string m_notecardUpdatePath = "0004/"; + private static readonly string m_notecardTaskUpdatePath = "0005/"; + // private static readonly string m_fetchInventoryPath = "0006/"; + + // The following entries are in a module, however, they are also here so that we don't re-assign + // the path to another cap by mistake. + // private static readonly string m_parcelVoiceInfoRequestPath = "0007/"; // This is in a module. + // private static readonly string m_provisionVoiceAccountRequestPath = "0008/";// This is in a module. + + // private static readonly string m_remoteParcelRequestPath = "0009/";// This is in the LandManagementModule. + + //private string eventQueue = "0100/"; + private IHttpServer m_httpListener; + private UUID m_agentID; + private IAssetService m_assetCache; + private int m_eventQueueCount = 1; + private Queue m_capsEventQueue = new Queue(); + private bool m_dumpAssetsToFile; + private string m_regionName; + + public bool SSLCaps + { + get { return m_httpListener.UseSSL; } + } + public string SSLCommonName + { + get { return m_httpListener.SSLCommonName; } + } + public CapsHandlers CapsHandlers + { + get { return m_capsHandlers; } + } + + // These are callbacks which will be setup by the scene so that we can update scene data when we + // receive capability calls + public NewInventoryItem AddNewInventoryItem = null; + public NewAsset AddNewAsset = null; + public ItemUpdatedCallback ItemUpdatedCall = null; + public TaskScriptUpdatedCallback TaskScriptUpdatedCall = null; + public FetchInventoryDescendentsCAPS CAPSFetchInventoryDescendents = null; + public GetClientDelegate GetClient = null; + + public Caps(IAssetService assetCache, IHttpServer httpServer, string httpListen, uint httpPort, string capsPath, + UUID agent, bool dumpAssetsToFile, string regionName) + { + m_assetCache = assetCache; + m_capsObjectPath = capsPath; + m_httpListener = httpServer; + m_httpListenerHostName = httpListen; + + m_httpListenPort = httpPort; + + if (httpServer.UseSSL) + { + m_httpListenPort = httpServer.SSLPort; + httpListen = httpServer.SSLCommonName; + httpPort = httpServer.SSLPort; + } + + m_agentID = agent; + m_dumpAssetsToFile = dumpAssetsToFile; + m_capsHandlers = new CapsHandlers(httpServer, httpListen, httpPort, httpServer.UseSSL); + m_regionName = regionName; + } + + /// + /// Register all CAPS http service handlers + /// + public void RegisterHandlers() + { + DeregisterHandlers(); + + string capsBase = "/CAPS/" + m_capsObjectPath; + + RegisterRegionServiceHandlers(capsBase); + RegisterInventoryServiceHandlers(capsBase); + + } + + public void RegisterRegionServiceHandlers(string capsBase) + { + try + { + // the root of all evil + m_capsHandlers["SEED"] = new RestStreamHandler("POST", capsBase + m_requestPath, CapsRequest); + m_log.DebugFormat( + "[CAPS]: Registered seed capability {0} for {1}", capsBase + m_requestPath, m_agentID); + + //m_capsHandlers["MapLayer"] = + // new LLSDStreamhandler("POST", + // capsBase + m_mapLayerPath, + // GetMapLayer); + m_capsHandlers["UpdateScriptTaskInventory"] = + new RestStreamHandler("POST", capsBase + m_notecardTaskUpdatePath, ScriptTaskInventory); + m_capsHandlers["UpdateScriptTask"] = m_capsHandlers["UpdateScriptTaskInventory"]; + + } + catch (Exception e) + { + m_log.Error("[CAPS]: " + e.ToString()); + } + } + + public void RegisterInventoryServiceHandlers(string capsBase) + { + try + { + // I don't think this one works... + m_capsHandlers["NewFileAgentInventory"] = + new LLSDStreamhandler("POST", + capsBase + m_newInventory, + NewAgentInventoryRequest); + m_capsHandlers["UpdateNotecardAgentInventory"] = + new RestStreamHandler("POST", capsBase + m_notecardUpdatePath, NoteCardAgentInventory); + m_capsHandlers["UpdateScriptAgentInventory"] = m_capsHandlers["UpdateNotecardAgentInventory"]; + m_capsHandlers["UpdateScriptAgent"] = m_capsHandlers["UpdateScriptAgentInventory"]; + + // As of RC 1.22.9 of the Linden client this is + // supported + + // m_capsHandlers["WebFetchInventoryDescendents"] =new RestStreamHandler("POST", capsBase + m_fetchInventoryPath, FetchInventoryDescendentsRequest); + + // justincc: I've disabled the CAPS service for now to fix problems with selecting textures, and + // subsequent inventory breakage, in the edit object pane (such as mantis 1085). This requires + // enhancements (probably filling out the folder part of the LLSD reply) to our CAPS service, + // but when I went on the Linden grid, the + // simulators I visited (version 1.21) were, surprisingly, no longer supplying this capability. Instead, + // the 1.19.1.4 client appeared to be happily flowing inventory data over UDP + // + // This is very probably just a temporary measure - once the CAPS service appears again on the Linden grid + // we will be + // able to get the data we need to implement the necessary part of the protocol to fix the issue above. + // m_capsHandlers["FetchInventoryDescendents"] = + // new RestStreamHandler("POST", capsBase + m_fetchInventoryPath, FetchInventoryRequest); + + // m_capsHandlers["FetchInventoryDescendents"] = + // new LLSDStreamhandler("POST", + // capsBase + m_fetchInventory, + // FetchInventory)); + // m_capsHandlers["RequestTextureDownload"] = new RestStreamHandler("POST", + // capsBase + m_requestTexture, + // RequestTexture); + } + catch (Exception e) + { + m_log.Error("[CAPS]: " + e.ToString()); + } + } + + /// + /// Register a handler. This allows modules to register handlers. + /// + /// + /// + public void RegisterHandler(string capName, IRequestHandler handler) + { + m_capsHandlers[capName] = handler; + //m_log.DebugFormat("[CAPS]: Registering handler for \"{0}\": path {1}", capName, handler.Path); + } + + /// + /// Remove all CAPS service handlers. + /// + /// + /// + /// + /// + public void DeregisterHandlers() + { + if (m_capsHandlers != null) + { + foreach (string capsName in m_capsHandlers.Caps) + { + m_capsHandlers.Remove(capsName); + } + } + } + + /// + /// Construct a client response detailing all the capabilities this server can provide. + /// + /// + /// + /// + /// HTTP request header object + /// HTTP response header object + /// + public string CapsRequest(string request, string path, string param, + OSHttpRequest httpRequest, OSHttpResponse httpResponse) + { + //m_log.Debug("[CAPS]: Seed Caps Request in region: " + m_regionName); + + string result = LLSDHelpers.SerialiseLLSDReply(m_capsHandlers.CapsDetails); + + //m_log.DebugFormat("[CAPS] CapsRequest {0}", result); + + return result; + } + + // FIXME: these all should probably go into the respective region + // modules + + /// + /// Processes a fetch inventory request and sends the reply + + /// + /// + /// + /// + /// + // Request is like: + // + // folders + // + // + // fetch-folders1fetch-items1folder-id8e1e3a30-b9bf-11dc-95ff-0800200c9a66owner-id11111111-1111-0000-0000-000100bba000sort-order1 + // + // + // + // + // + // multiple fetch-folder maps are allowed within the larger folders map. + public string FetchInventoryRequest(string request, string path, string param) + { + // string unmodifiedRequest = request.ToString(); + + //m_log.DebugFormat("[AGENT INVENTORY]: Received CAPS fetch inventory request {0}", unmodifiedRequest); + m_log.Debug("[CAPS]: Inventory Request in region: " + m_regionName); + + Hashtable hash = new Hashtable(); + try + { + hash = (Hashtable)LLSD.LLSDDeserialize(Utils.StringToBytes(request)); + } + catch (LLSD.LLSDParseException pe) + { + m_log.Error("[AGENT INVENTORY]: Fetch error: " + pe.Message); + m_log.Error("Request: " + request.ToString()); + } + + ArrayList foldersrequested = (ArrayList)hash["folders"]; + + string response = ""; + + for (int i = 0; i < foldersrequested.Count; i++) + { + string inventoryitemstr = ""; + Hashtable inventoryhash = (Hashtable)foldersrequested[i]; + + LLSDFetchInventoryDescendents llsdRequest = new LLSDFetchInventoryDescendents(); + LLSDHelpers.DeserialiseOSDMap(inventoryhash, llsdRequest); + LLSDInventoryDescendents reply = FetchInventoryReply(llsdRequest); + + inventoryitemstr = LLSDHelpers.SerialiseLLSDReply(reply); + inventoryitemstr = inventoryitemstr.Replace("folders", ""); + inventoryitemstr = inventoryitemstr.Replace("", ""); + + response += inventoryitemstr; + } + + if (response.Length == 0) + { + // Ter-guess: If requests fail a lot, the client seems to stop requesting descendants. + // Therefore, I'm concluding that the client only has so many threads available to do requests + // and when a thread stalls.. is stays stalled. + // Therefore we need to return something valid + response = "folders"; + } + else + { + response = "folders" + response + ""; + } + + //m_log.DebugFormat("[AGENT INVENTORY]: Replying to CAPS fetch inventory request with following xml"); + //m_log.Debug(Util.GetFormattedXml(response)); + + return response; + } + + public string FetchInventoryDescendentsRequest(string request, string path, string param,OSHttpRequest httpRequest, OSHttpResponse httpResponse) + { + // m_log.Debug("[CAPS]: FetchInventoryDescendentsRequest in region: " + m_regionName + "request is "+request); + + // nasty temporary hack here, the linden client falsely identifies the uuid 00000000-0000-0000-0000-000000000000 as a string which breaks us + // correctly mark it as a uuid + request = request.Replace("00000000-0000-0000-0000-000000000000", "00000000-0000-0000-0000-000000000000"); + + // another hack 1 results in a System.ArgumentException: Object type System.Int32 cannot be converted to target type: System.Boolean + request = request.Replace("fetch_folders0", "fetch_folders0"); + request = request.Replace("fetch_folders1", "fetch_folders1"); + Hashtable hash = new Hashtable(); + try + { + hash = (Hashtable)LLSD.LLSDDeserialize(Utils.StringToBytes(request)); + } + catch (LLSD.LLSDParseException pe) + { + m_log.Error("[AGENT INVENTORY]: Fetch error: " + pe.Message); + m_log.Error("Request: " + request.ToString()); + } + + ArrayList foldersrequested = (ArrayList)hash["folders"]; + + string response = ""; + for (int i = 0; i < foldersrequested.Count; i++) + { + string inventoryitemstr = ""; + Hashtable inventoryhash = (Hashtable)foldersrequested[i]; + + LLSDFetchInventoryDescendents llsdRequest = new LLSDFetchInventoryDescendents(); + + try{ + LLSDHelpers.DeserialiseOSDMap(inventoryhash, llsdRequest); + } + catch(Exception e) + { + m_log.Debug("[CAPS]: caught exception doing OSD deserialize" + e); + } + LLSDInventoryDescendents reply = FetchInventoryReply(llsdRequest); + + inventoryitemstr = LLSDHelpers.SerialiseLLSDReply(reply); + inventoryitemstr = inventoryitemstr.Replace("folders", ""); + inventoryitemstr = inventoryitemstr.Replace("", ""); + + response += inventoryitemstr; + } + + + if (response.Length == 0) + { + // Ter-guess: If requests fail a lot, the client seems to stop requesting descendants. + // Therefore, I'm concluding that the client only has so many threads available to do requests + // and when a thread stalls.. is stays stalled. + // Therefore we need to return something valid + response = "folders"; + } + else + { + response = "folders" + response + ""; + } + + //m_log.DebugFormat("[CAPS]: Replying to CAPS fetch inventory request with following xml"); + //m_log.Debug("[CAPS] "+response); + + return response; + } + + + + /// + /// Construct an LLSD reply packet to a CAPS inventory request + /// + /// + /// + private LLSDInventoryDescendents FetchInventoryReply(LLSDFetchInventoryDescendents invFetch) + { + LLSDInventoryDescendents reply = new LLSDInventoryDescendents(); + LLSDInventoryFolderContents contents = new LLSDInventoryFolderContents(); + contents.agent_id = m_agentID; + contents.owner_id = invFetch.owner_id; + contents.folder_id = invFetch.folder_id; + + // The version number being sent back was originally 1. + // Unfortunately, on 1.19.1.4, this means that we see a problem where on subsequent logins + // without clearing client cache, objects in the root folder disappear until the cache is cleared, + // at which point they reappear. + // + // Seeing the version to something other than 0 may be the right thing to do, but there is + // a greater subtlety of the second life protocol that needs to be understood first. + contents.version = 0; + + contents.descendents = 0; + reply.folders.Array.Add(contents); + List itemList = null; + if (CAPSFetchInventoryDescendents != null) + { + itemList = CAPSFetchInventoryDescendents(m_agentID, invFetch.folder_id, invFetch.owner_id, invFetch.fetch_folders, invFetch.fetch_items, invFetch.sort_order); + } + + if (itemList != null) + { + foreach (InventoryItemBase invItem in itemList) + { + contents.items.Array.Add(ConvertInventoryItem(invItem)); + } + } + /* The following block is removed as it ALWAYS sends the error to the client because the RC 1.22.9 client tries to + find items that have become dissasociated with a paret folder and have parent of 00000000-0000-00000.... + else + { + IClientAPI client = GetClient(m_agentID); + + // We're going to both notify the client of inventory service failure and send back a 'no folder contents' response. + // If we don't send back the response, + // the client becomes unhappy (see Teravus' comment in FetchInventoryRequest()) + if (client != null) + { + client.SendAgentAlertMessage( + "AGIN0001E: The inventory service has either failed or is not responding. Your inventory will not function properly for the rest of this session. Please clear your cache and relog.", + true); + } + else + { + m_log.ErrorFormat( + "[AGENT INVENTORY]: Could not lookup controlling client for {0} in order to notify them of the inventory service failure", + m_agentID); + } + }*/ + + contents.descendents = contents.items.Array.Count; + return reply; + } + + /// + /// Convert an internal inventory item object into an LLSD object. + /// + /// + /// + private LLSDInventoryItem ConvertInventoryItem(InventoryItemBase invItem) + { + LLSDInventoryItem llsdItem = new LLSDInventoryItem(); + llsdItem.asset_id = invItem.AssetID; + llsdItem.created_at = invItem.CreationDate; + llsdItem.desc = invItem.Description; + llsdItem.flags = 0; + llsdItem.item_id = invItem.ID; + llsdItem.name = invItem.Name; + llsdItem.parent_id = invItem.Folder; + try + { + // TODO reevaluate after upgrade to libomv >= r2566. Probably should use UtilsConversions. + llsdItem.type = TaskInventoryItem.Types[invItem.AssetType]; + llsdItem.inv_type = TaskInventoryItem.InvTypes[invItem.InvType]; + } + catch (Exception e) + { + m_log.Error("[CAPS]: Problem setting asset/inventory type while converting inventory item " + invItem.Name + " to LLSD:", e); + } + llsdItem.permissions = new LLSDPermissions(); + llsdItem.permissions.creator_id = invItem.CreatorIdAsUuid; + llsdItem.permissions.base_mask = (int)invItem.CurrentPermissions; + llsdItem.permissions.everyone_mask = (int)invItem.EveryOnePermissions; + llsdItem.permissions.group_id = UUID.Zero; + llsdItem.permissions.group_mask = 0; + llsdItem.permissions.is_owner_group = false; + llsdItem.permissions.next_owner_mask = (int)invItem.NextPermissions; + llsdItem.permissions.owner_id = m_agentID; // FixMe + llsdItem.permissions.owner_mask = (int)invItem.CurrentPermissions; + llsdItem.sale_info = new LLSDSaleInfo(); + llsdItem.sale_info.sale_price = 10; + llsdItem.sale_info.sale_type = "not"; + + return llsdItem; + } + + /// + /// + /// + /// + /// + public LLSDMapLayerResponse GetMapLayer(LLSDMapRequest mapReq) + { + m_log.Debug("[CAPS]: MapLayer Request in region: " + m_regionName); + LLSDMapLayerResponse mapResponse = new LLSDMapLayerResponse(); + mapResponse.LayerData.Array.Add(GetOSDMapLayerResponse()); + return mapResponse; + } + + /// + /// + /// + /// + protected static OSDMapLayer GetOSDMapLayerResponse() + { + OSDMapLayer mapLayer = new OSDMapLayer(); + mapLayer.Right = 5000; + mapLayer.Top = 5000; + mapLayer.ImageID = new UUID("00000000-0000-1111-9999-000000000006"); + + return mapLayer; + } + + /// + /// + /// + /// + /// + /// + /// + public string RequestTexture(string request, string path, string param) + { + m_log.Debug("texture request " + request); + // Needs implementing (added to remove compiler warning) + return String.Empty; + } + + #region EventQueue (Currently not enabled) + + /// + /// + /// + /// + /// + /// + /// + public string ProcessEventQueue(string request, string path, string param) + { + string res = String.Empty; + + if (m_capsEventQueue.Count > 0) + { + lock (m_capsEventQueue) + { + string item = m_capsEventQueue.Dequeue(); + res = item; + } + } + else + { + res = CreateEmptyEventResponse(); + } + return res; + } + + /// + /// + /// + /// + /// + /// + public string CreateEstablishAgentComms(string caps, string ipAddressPort) + { + LLSDCapEvent eventItem = new LLSDCapEvent(); + eventItem.id = m_eventQueueCount; + //should be creating a EstablishAgentComms item, but there isn't a class for it yet + eventItem.events.Array.Add(new LLSDEmpty()); + string res = LLSDHelpers.SerialiseLLSDReply(eventItem); + m_eventQueueCount++; + + m_capsEventQueue.Enqueue(res); + return res; + } + + /// + /// + /// + /// + public string CreateEmptyEventResponse() + { + LLSDCapEvent eventItem = new LLSDCapEvent(); + eventItem.id = m_eventQueueCount; + eventItem.events.Array.Add(new LLSDEmpty()); + string res = LLSDHelpers.SerialiseLLSDReply(eventItem); + m_eventQueueCount++; + return res; + } + + #endregion + + /// + /// Called by the script task update handler. Provides a URL to which the client can upload a new asset. + /// + /// + /// + /// + /// HTTP request header object + /// HTTP response header object + /// + public string ScriptTaskInventory(string request, string path, string param, + OSHttpRequest httpRequest, OSHttpResponse httpResponse) + { + try + { + m_log.Debug("[CAPS]: ScriptTaskInventory Request in region: " + m_regionName); + //m_log.DebugFormat("[CAPS]: request: {0}, path: {1}, param: {2}", request, path, param); + + Hashtable hash = (Hashtable) LLSD.LLSDDeserialize(Utils.StringToBytes(request)); + LLSDTaskScriptUpdate llsdUpdateRequest = new LLSDTaskScriptUpdate(); + LLSDHelpers.DeserialiseOSDMap(hash, llsdUpdateRequest); + + string capsBase = "/CAPS/" + m_capsObjectPath; + string uploaderPath = Util.RandomClass.Next(5000, 8000).ToString("0000"); + + TaskInventoryScriptUpdater uploader = + new TaskInventoryScriptUpdater( + llsdUpdateRequest.item_id, + llsdUpdateRequest.task_id, + llsdUpdateRequest.is_script_running, + capsBase + uploaderPath, + m_httpListener, + m_dumpAssetsToFile); + uploader.OnUpLoad += TaskScriptUpdated; + + m_httpListener.AddStreamHandler( + new BinaryStreamHandler("POST", capsBase + uploaderPath, uploader.uploaderCaps)); + + string protocol = "http://"; + + if (m_httpListener.UseSSL) + protocol = "https://"; + + string uploaderURL = protocol + m_httpListenerHostName + ":" + m_httpListenPort.ToString() + capsBase + + uploaderPath; + + LLSDAssetUploadResponse uploadResponse = new LLSDAssetUploadResponse(); + uploadResponse.uploader = uploaderURL; + uploadResponse.state = "upload"; + +// m_log.InfoFormat("[CAPS]: " + +// "ScriptTaskInventory response: {0}", +// LLSDHelpers.SerialiseLLSDReply(uploadResponse))); + + return LLSDHelpers.SerialiseLLSDReply(uploadResponse); + } + catch (Exception e) + { + m_log.Error("[CAPS]: " + e.ToString()); + } + + return null; + } + + /// + /// Called by the notecard update handler. Provides a URL to which the client can upload a new asset. + /// + /// + /// + /// + /// + public string NoteCardAgentInventory(string request, string path, string param, + OSHttpRequest httpRequest, OSHttpResponse httpResponse) + { + //m_log.Debug("[CAPS]: NoteCardAgentInventory Request in region: " + m_regionName + "\n" + request); + //m_log.Debug("[CAPS]: NoteCardAgentInventory Request is: " + request); + + //OpenMetaverse.StructuredData.OSDMap hash = (OpenMetaverse.StructuredData.OSDMap)OpenMetaverse.StructuredData.LLSDParser.DeserializeBinary(Utils.StringToBytes(request)); + Hashtable hash = (Hashtable) LLSD.LLSDDeserialize(Utils.StringToBytes(request)); + LLSDItemUpdate llsdRequest = new LLSDItemUpdate(); + LLSDHelpers.DeserialiseOSDMap(hash, llsdRequest); + + string capsBase = "/CAPS/" + m_capsObjectPath; + string uploaderPath = Util.RandomClass.Next(5000, 8000).ToString("0000"); + + ItemUpdater uploader = + new ItemUpdater(llsdRequest.item_id, capsBase + uploaderPath, m_httpListener, m_dumpAssetsToFile); + uploader.OnUpLoad += ItemUpdated; + + m_httpListener.AddStreamHandler( + new BinaryStreamHandler("POST", capsBase + uploaderPath, uploader.uploaderCaps)); + + string protocol = "http://"; + + if (m_httpListener.UseSSL) + protocol = "https://"; + + string uploaderURL = protocol + m_httpListenerHostName + ":" + m_httpListenPort.ToString() + capsBase + + uploaderPath; + + LLSDAssetUploadResponse uploadResponse = new LLSDAssetUploadResponse(); + uploadResponse.uploader = uploaderURL; + uploadResponse.state = "upload"; + +// m_log.InfoFormat("[CAPS]: " + +// "NoteCardAgentInventory response: {0}", +// LLSDHelpers.SerialiseLLSDReply(uploadResponse))); + + return LLSDHelpers.SerialiseLLSDReply(uploadResponse); + } + + /// + /// + /// + /// + /// + public LLSDAssetUploadResponse NewAgentInventoryRequest(LLSDAssetUploadRequest llsdRequest) + { + //m_log.Debug("[CAPS]: NewAgentInventoryRequest Request is: " + llsdRequest.ToString()); + //m_log.Debug("asset upload request via CAPS" + llsdRequest.inventory_type + " , " + llsdRequest.asset_type); + + if (llsdRequest.asset_type == "texture" || + llsdRequest.asset_type == "animation" || + llsdRequest.asset_type == "sound") + { + IClientAPI client = null; + IScene scene = null; + if (GetClient != null) + { + client = GetClient(m_agentID); + scene = client.Scene; + + IMoneyModule mm = scene.RequestModuleInterface(); + + if (mm != null) + { + if (!mm.UploadCovered(client)) + { + if (client != null) + client.SendAgentAlertMessage("Unable to upload asset. Insufficient funds.", false); + + LLSDAssetUploadResponse errorResponse = new LLSDAssetUploadResponse(); + errorResponse.uploader = ""; + errorResponse.state = "error"; + return errorResponse; + } + } + } + } + + + string assetName = llsdRequest.name; + string assetDes = llsdRequest.description; + string capsBase = "/CAPS/" + m_capsObjectPath; + UUID newAsset = UUID.Random(); + UUID newInvItem = UUID.Random(); + UUID parentFolder = llsdRequest.folder_id; + string uploaderPath = Util.RandomClass.Next(5000, 8000).ToString("0000"); + + AssetUploader uploader = + new AssetUploader(assetName, assetDes, newAsset, newInvItem, parentFolder, llsdRequest.inventory_type, + llsdRequest.asset_type, capsBase + uploaderPath, m_httpListener, m_dumpAssetsToFile); + m_httpListener.AddStreamHandler( + new BinaryStreamHandler("POST", capsBase + uploaderPath, uploader.uploaderCaps)); + + string protocol = "http://"; + + if (m_httpListener.UseSSL) + protocol = "https://"; + + string uploaderURL = protocol + m_httpListenerHostName + ":" + m_httpListenPort.ToString() + capsBase + + uploaderPath; + + LLSDAssetUploadResponse uploadResponse = new LLSDAssetUploadResponse(); + uploadResponse.uploader = uploaderURL; + uploadResponse.state = "upload"; + uploader.OnUpLoad += UploadCompleteHandler; + return uploadResponse; + } + + /// + /// + /// + /// + /// + /// + public void UploadCompleteHandler(string assetName, string assetDescription, UUID assetID, + UUID inventoryItem, UUID parentFolder, byte[] data, string inventoryType, + string assetType) + { + sbyte assType = 0; + sbyte inType = 0; + + if (inventoryType == "sound") + { + inType = 1; + assType = 1; + } + else if (inventoryType == "animation") + { + inType = 19; + assType = 20; + } + else if (inventoryType == "wearable") + { + inType = 18; + switch (assetType) + { + case "bodypart": + assType = 13; + break; + case "clothing": + assType = 5; + break; + } + } + + AssetBase asset; + asset = new AssetBase(); + asset.FullID = assetID; + asset.Type = assType; + asset.Name = assetName; + asset.Data = data; + if (AddNewAsset != null) + AddNewAsset(asset); + else if (m_assetCache != null) + m_assetCache.Store(asset); + + InventoryItemBase item = new InventoryItemBase(); + item.Owner = m_agentID; + item.CreatorId = m_agentID.ToString(); + item.ID = inventoryItem; + item.AssetID = asset.FullID; + item.Description = assetDescription; + item.Name = assetName; + item.AssetType = assType; + item.InvType = inType; + item.Folder = parentFolder; + item.CurrentPermissions = 2147483647; + item.BasePermissions = 2147483647; + item.EveryOnePermissions = 0; + item.NextPermissions = 2147483647; + item.CreationDate = Util.UnixTimeSinceEpoch(); + + if (AddNewInventoryItem != null) + { + AddNewInventoryItem(m_agentID, item); + } + } + + /// + /// Called when new asset data for an agent inventory item update has been uploaded. + /// + /// Item to update + /// New asset data + /// + public UUID ItemUpdated(UUID itemID, byte[] data) + { + if (ItemUpdatedCall != null) + { + return ItemUpdatedCall(m_agentID, itemID, data); + } + + return UUID.Zero; + } + + /// + /// Called when new asset data for an agent inventory item update has been uploaded. + /// + /// Item to update + /// Prim containing item to update + /// Signals whether the script to update is currently running + /// New asset data + public void TaskScriptUpdated(UUID itemID, UUID primID, bool isScriptRunning, byte[] data) + { + if (TaskScriptUpdatedCall != null) + { + TaskScriptUpdatedCall(m_agentID, itemID, primID, isScriptRunning, data); + } + } + + public class AssetUploader + { + public event UpLoadedAsset OnUpLoad; + private UpLoadedAsset handlerUpLoad = null; + + private string uploaderPath = String.Empty; + private UUID newAssetID; + private UUID inventoryItemID; + private UUID parentFolder; + private IHttpServer httpListener; + private bool m_dumpAssetsToFile; + private string m_assetName = String.Empty; + private string m_assetDes = String.Empty; + + private string m_invType = String.Empty; + private string m_assetType = String.Empty; + + public AssetUploader(string assetName, string description, UUID assetID, UUID inventoryItem, + UUID parentFolderID, string invType, string assetType, string path, + IHttpServer httpServer, bool dumpAssetsToFile) + { + m_assetName = assetName; + m_assetDes = description; + newAssetID = assetID; + inventoryItemID = inventoryItem; + uploaderPath = path; + httpListener = httpServer; + parentFolder = parentFolderID; + m_assetType = assetType; + m_invType = invType; + m_dumpAssetsToFile = dumpAssetsToFile; + } + + /// + /// + /// + /// + /// + /// + /// + public string uploaderCaps(byte[] data, string path, string param) + { + UUID inv = inventoryItemID; + string res = String.Empty; + LLSDAssetUploadComplete uploadComplete = new LLSDAssetUploadComplete(); + uploadComplete.new_asset = newAssetID.ToString(); + uploadComplete.new_inventory_item = inv; + uploadComplete.state = "complete"; + + res = LLSDHelpers.SerialiseLLSDReply(uploadComplete); + + httpListener.RemoveStreamHandler("POST", uploaderPath); + + // TODO: probably make this a better set of extensions here + string extension = ".jp2"; + if (m_invType != "image") + { + extension = ".dat"; + } + + if (m_dumpAssetsToFile) + { + SaveAssetToFile(m_assetName + extension, data); + } + handlerUpLoad = OnUpLoad; + if (handlerUpLoad != null) + { + handlerUpLoad(m_assetName, m_assetDes, newAssetID, inv, parentFolder, data, m_invType, m_assetType); + } + + return res; + } + ///Left this in and commented in case there are unforseen issues + //private void SaveAssetToFile(string filename, byte[] data) + //{ + // FileStream fs = File.Create(filename); + // BinaryWriter bw = new BinaryWriter(fs); + // bw.Write(data); + // bw.Close(); + // fs.Close(); + //} + private static void SaveAssetToFile(string filename, byte[] data) + { + string assetPath = "UserAssets"; + if (!Directory.Exists(assetPath)) + { + Directory.CreateDirectory(assetPath); + } + FileStream fs = File.Create(Path.Combine(assetPath, Util.safeFileName(filename))); + BinaryWriter bw = new BinaryWriter(fs); + bw.Write(data); + bw.Close(); + fs.Close(); + } + } + + /// + /// This class is a callback invoked when a client sends asset data to + /// an agent inventory notecard update url + /// + public class ItemUpdater + { + public event UpdateItem OnUpLoad; + + private UpdateItem handlerUpdateItem = null; + + private string uploaderPath = String.Empty; + private UUID inventoryItemID; + private IHttpServer httpListener; + private bool m_dumpAssetToFile; + + public ItemUpdater(UUID inventoryItem, string path, IHttpServer httpServer, bool dumpAssetToFile) + { + m_dumpAssetToFile = dumpAssetToFile; + + inventoryItemID = inventoryItem; + uploaderPath = path; + httpListener = httpServer; + } + + /// + /// + /// + /// + /// + /// + /// + public string uploaderCaps(byte[] data, string path, string param) + { + UUID inv = inventoryItemID; + string res = String.Empty; + LLSDAssetUploadComplete uploadComplete = new LLSDAssetUploadComplete(); + UUID assetID = UUID.Zero; + handlerUpdateItem = OnUpLoad; + if (handlerUpdateItem != null) + { + assetID = handlerUpdateItem(inv, data); + } + + uploadComplete.new_asset = assetID.ToString(); + uploadComplete.new_inventory_item = inv; + uploadComplete.state = "complete"; + + res = LLSDHelpers.SerialiseLLSDReply(uploadComplete); + + httpListener.RemoveStreamHandler("POST", uploaderPath); + + if (m_dumpAssetToFile) + { + SaveAssetToFile("updateditem" + Util.RandomClass.Next(1, 1000) + ".dat", data); + } + + return res; + } + ///Left this in and commented in case there are unforseen issues + //private void SaveAssetToFile(string filename, byte[] data) + //{ + // FileStream fs = File.Create(filename); + // BinaryWriter bw = new BinaryWriter(fs); + // bw.Write(data); + // bw.Close(); + // fs.Close(); + //} + private static void SaveAssetToFile(string filename, byte[] data) + { + string assetPath = "UserAssets"; + if (!Directory.Exists(assetPath)) + { + Directory.CreateDirectory(assetPath); + } + FileStream fs = File.Create(Path.Combine(assetPath, filename)); + BinaryWriter bw = new BinaryWriter(fs); + bw.Write(data); + bw.Close(); + fs.Close(); + } + } + + /// + /// This class is a callback invoked when a client sends asset data to + /// a task inventory script update url + /// + public class TaskInventoryScriptUpdater + { + public event UpdateTaskScript OnUpLoad; + + private UpdateTaskScript handlerUpdateTaskScript = null; + + private string uploaderPath = String.Empty; + private UUID inventoryItemID; + private UUID primID; + private bool isScriptRunning; + private IHttpServer httpListener; + private bool m_dumpAssetToFile; + + public TaskInventoryScriptUpdater(UUID inventoryItemID, UUID primID, int isScriptRunning, + string path, IHttpServer httpServer, bool dumpAssetToFile) + { + m_dumpAssetToFile = dumpAssetToFile; + + this.inventoryItemID = inventoryItemID; + this.primID = primID; + + // This comes in over the packet as an integer, but actually appears to be treated as a bool + this.isScriptRunning = (0 == isScriptRunning ? false : true); + + uploaderPath = path; + httpListener = httpServer; + } + + /// + /// + /// + /// + /// + /// + /// + public string uploaderCaps(byte[] data, string path, string param) + { + try + { +// m_log.InfoFormat("[CAPS]: " + +// "TaskInventoryScriptUpdater received data: {0}, path: {1}, param: {2}", +// data, path, param)); + + string res = String.Empty; + LLSDTaskInventoryUploadComplete uploadComplete = new LLSDTaskInventoryUploadComplete(); + + handlerUpdateTaskScript = OnUpLoad; + if (handlerUpdateTaskScript != null) + { + handlerUpdateTaskScript(inventoryItemID, primID, isScriptRunning, data); + } + + uploadComplete.item_id = inventoryItemID; + uploadComplete.task_id = primID; + uploadComplete.state = "complete"; + + res = LLSDHelpers.SerialiseLLSDReply(uploadComplete); + + httpListener.RemoveStreamHandler("POST", uploaderPath); + + if (m_dumpAssetToFile) + { + SaveAssetToFile("updatedtaskscript" + Util.RandomClass.Next(1, 1000) + ".dat", data); + } + +// m_log.InfoFormat("[CAPS]: TaskInventoryScriptUpdater.uploaderCaps res: {0}", res); + + return res; + } + catch (Exception e) + { + m_log.Error("[CAPS]: " + e.ToString()); + } + + // XXX Maybe this should be some meaningful error packet + return null; + } + ///Left this in and commented in case there are unforseen issues + //private void SaveAssetToFile(string filename, byte[] data) + //{ + // FileStream fs = File.Create(filename); + // BinaryWriter bw = new BinaryWriter(fs); + // bw.Write(data); + // bw.Close(); + // fs.Close(); + //} + private static void SaveAssetToFile(string filename, byte[] data) + { + string assetPath = "UserAssets"; + if (!Directory.Exists(assetPath)) + { + Directory.CreateDirectory(assetPath); + } + FileStream fs = File.Create(Path.Combine(assetPath, filename)); + BinaryWriter bw = new BinaryWriter(fs); + bw.Write(data); + bw.Close(); + fs.Close(); + } + } + } +} -- cgit v1.1