From eeb5381bbbf58190824d047d845569564368c708 Mon Sep 17 00:00:00 2001
From: Dr Scofield
Date: Thu, 18 Sep 2008 15:50:52 +0000
Subject: and this actually adds the appearance code itself (and not just the
check-in message)
---
.../Rest/Inventory/RestAppearanceServices.cs | 826 +++++++++++++++++++++
OpenSim/ApplicationPlugins/Rest/rest.xsd | 276 +++++++
2 files changed, 1102 insertions(+)
create mode 100644 OpenSim/ApplicationPlugins/Rest/Inventory/RestAppearanceServices.cs
create mode 100644 OpenSim/ApplicationPlugins/Rest/rest.xsd
diff --git a/OpenSim/ApplicationPlugins/Rest/Inventory/RestAppearanceServices.cs b/OpenSim/ApplicationPlugins/Rest/Inventory/RestAppearanceServices.cs
new file mode 100644
index 0000000..bc706f0
--- /dev/null
+++ b/OpenSim/ApplicationPlugins/Rest/Inventory/RestAppearanceServices.cs
@@ -0,0 +1,826 @@
+/*
+ * 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 OpenSim 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.Threading;
+using System.Xml;
+using System.Drawing;
+using OpenSim.Framework;
+using OpenSim.Framework.Servers;
+using OpenSim.Framework.Communications;
+using OpenSim.Framework.Communications.Cache;
+using OpenMetaverse;
+using OpenMetaverse.Imaging;
+using Nini.Config;
+
+namespace OpenSim.ApplicationPlugins.Rest.Inventory
+{
+ public class RestAppearanceServices : IRest
+ {
+ private static readonly int PARM_USERID = 0;
+ private static readonly int PARM_PATH = 1;
+
+ private bool enabled = false;
+ private string qPrefix = "appearance";
+
+ ///
+ /// The constructor makes sure that the service prefix is absolute
+ /// and the registers the service handler and the allocator.
+ ///
+
+ public RestAppearanceServices()
+ {
+ Rest.Log.InfoFormat("{0} User appearance services initializing", MsgId);
+ Rest.Log.InfoFormat("{0} Using REST Implementation Version {1}", MsgId, Rest.Version);
+
+ // If a relative path was specified for the handler's domain,
+ // add the standard prefix to make it absolute, e.g. /admin
+
+ if (!qPrefix.StartsWith(Rest.UrlPathSeparator))
+ {
+ Rest.Log.InfoFormat("{0} Domain is relative, adding absolute prefix", MsgId);
+ qPrefix = String.Format("{0}{1}{2}", Rest.Prefix, Rest.UrlPathSeparator, qPrefix);
+ Rest.Log.InfoFormat("{0} Domain is now <{1}>", MsgId, qPrefix);
+ }
+
+ // Register interface using the absolute URI.
+
+ Rest.Plugin.AddPathHandler(DoAppearance,qPrefix,Allocate);
+
+ // Activate if everything went OK
+
+ enabled = true;
+
+ Rest.Log.InfoFormat("{0} User appearance services initialization complete", MsgId);
+ }
+
+ ///
+ /// Post-construction, pre-enabled initialization opportunity
+ /// Not currently exploited.
+ ///
+
+ public void Initialize()
+ {
+ }
+
+ ///
+ /// Called by the plug-in to halt service processing. Local processing is
+ /// disabled.
+ ///
+
+ public void Close()
+ {
+ enabled = false;
+ Rest.Log.InfoFormat("{0} User appearance services closing down", MsgId);
+ }
+
+ ///
+ /// This property is declared locally because it is used a lot and
+ /// brevity is nice.
+ ///
+
+ internal string MsgId
+ {
+ get { return Rest.MsgId; }
+ }
+
+ #region Interface
+
+ ///
+ /// The plugin (RestHandler) calls this method to allocate the request
+ /// state carrier for a new request. It is destroyed when the request
+ /// completes. All request-instance specific state is kept here. This
+ /// is registered when this service provider is registered.
+ ///
+ /// Inbound HTTP request information
+ /// Outbound HTTP request information
+ /// REST service domain prefix
+ /// A RequestData instance suitable for this service
+
+ private RequestData Allocate(OSHttpRequest request, OSHttpResponse response, string prefix)
+ {
+ return (RequestData) new AppearanceRequestData(request, response, prefix);
+ }
+
+ ///
+ /// This method is registered with the handler when this service provider
+ /// is initialized. It is called whenever the plug-in identifies this service
+ /// provider as the best match for a given request.
+ /// It handles all aspects of inventory REST processing, i.e. /admin/inventory
+ ///
+ /// A consolidated HTTP request work area
+
+ private void DoAppearance(RequestData hdata)
+ {
+ AppearanceRequestData rdata = (AppearanceRequestData) hdata;
+
+ Rest.Log.DebugFormat("{0} DoAppearance ENTRY", MsgId);
+
+ // If we're disabled, do nothing.
+
+ if (!enabled)
+ {
+ return;
+ }
+
+ // Now that we know this is a serious attempt to
+ // access inventory data, we should find out who
+ // is asking, and make sure they are authorized
+ // to do so. We need to validate the caller's
+ // identity before revealing anything about the
+ // status quo. Authenticate throws an exception
+ // via Fail if no identity information is present.
+ //
+ // With the present HTTP server we can't use the
+ // builtin authentication mechanisms because they
+ // would be enforced for all in-bound requests.
+ // Instead we look at the headers ourselves and
+ // handle authentication directly.
+
+ try
+ {
+ if (!rdata.IsAuthenticated)
+ {
+ rdata.Fail(Rest.HttpStatusCodeNotAuthorized,String.Format("user \"{0}\" could not be authenticated", rdata.userName));
+ }
+ }
+ catch (RestException e)
+ {
+ if (e.statusCode == Rest.HttpStatusCodeNotAuthorized)
+ {
+ Rest.Log.WarnFormat("{0} User not authenticated", MsgId);
+ Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId, rdata.request.Headers.Get("Authorization"));
+ }
+ else
+ {
+ Rest.Log.ErrorFormat("{0} User authentication failed", MsgId);
+ Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId, rdata.request.Headers.Get("Authorization"));
+ }
+ throw (e);
+ }
+
+ Rest.Log.DebugFormat("{0} Authenticated {1}", MsgId, rdata.userName);
+
+ // We can only get here if we are authorized
+ //
+ // The requestor may have specified an UUID or
+ // a conjoined FirstName LastName string. We'll
+ // try both. If we fail with the first, UUID,
+ // attempt, we try the other. As an example, the
+ // URI for a valid inventory request might be:
+ //
+ // http://:/admin/inventory/Arthur Dent
+ //
+ // Indicating that this is an inventory request for
+ // an avatar named Arthur Dent. This is ALL that is
+ // required to designate a GET for an entire
+ // inventory.
+ //
+
+
+ // Do we have at least a user agent name?
+
+ if (rdata.Parameters.Length < 1)
+ {
+ Rest.Log.WarnFormat("{0} Appearance: No user agent identifier specified", MsgId);
+ rdata.Fail(Rest.HttpStatusCodeBadRequest, "no user identity specified");
+ }
+
+ // The first parameter MUST be the agent identification, either an UUID
+ // or a space-separated First-name Last-Name specification. We check for
+ // an UUID first, if anyone names their character using a valid UUID
+ // that identifies another existing avatar will cause this a problem...
+
+ try
+ {
+ rdata.uuid = new UUID(rdata.Parameters[PARM_USERID]);
+ Rest.Log.DebugFormat("{0} UUID supplied", MsgId);
+ rdata.userProfile = Rest.UserServices.GetUserProfile(rdata.uuid);
+ }
+ catch
+ {
+ string[] names = rdata.Parameters[PARM_USERID].Split(Rest.CA_SPACE);
+ if (names.Length == 2)
+ {
+ Rest.Log.DebugFormat("{0} Agent Name supplied [2]", MsgId);
+ rdata.userProfile = Rest.UserServices.GetUserProfile(names[0],names[1]);
+ }
+ else
+ {
+ Rest.Log.WarnFormat("{0} A Valid UUID or both first and last names must be specified", MsgId);
+ rdata.Fail(Rest.HttpStatusCodeBadRequest, "invalid user identity");
+ }
+ }
+
+ // If the user profile is null then either the server is broken, or the
+ // user is not known. We always assume the latter case.
+
+ if (rdata.userProfile != null)
+ {
+ Rest.Log.DebugFormat("{0} User profile obtained for agent {1} {2}",
+ MsgId, rdata.userProfile.FirstName, rdata.userProfile.SurName);
+ }
+ else
+ {
+ Rest.Log.WarnFormat("{0} No user profile for {1}", MsgId, rdata.path);
+ rdata.Fail(Rest.HttpStatusCodeNotFound, "unrecognized user identity");
+ }
+
+ // If we get to here, then we have effectively validated the user's
+
+ switch (rdata.method)
+ {
+ case Rest.HEAD : // Do the processing, set the status code, suppress entity
+ DoGet(rdata);
+ rdata.buffer = null;
+ break;
+
+ case Rest.GET : // Do the processing, set the status code, return entity
+ DoGet(rdata);
+ break;
+
+ case Rest.PUT : // Update named element
+ DoUpdate(rdata);
+ break;
+
+ case Rest.POST : // Add new information to identified context.
+ DoExtend(rdata);
+ break;
+
+ case Rest.DELETE : // Delete information
+ DoDelete(rdata);
+ break;
+
+ default :
+ Rest.Log.WarnFormat("{0} Method {1} not supported for {2}",
+ MsgId, rdata.method, rdata.path);
+ rdata.Fail(Rest.HttpStatusCodeMethodNotAllowed,
+ String.Format("{0} not supported", rdata.method));
+ break;
+ }
+ }
+
+ #endregion Interface
+
+ #region method-specific processing
+
+ ///
+ /// This method implements GET processing for user's appearance.
+ ///
+ /// HTTP service request work area
+
+ private void DoGet(AppearanceRequestData rdata)
+ {
+
+ rdata.userAppearance = Rest.AvatarServices.GetUserAppearance(rdata.userProfile.ID);
+
+ if (rdata.userAppearance == null)
+ {
+ rdata.Fail(Rest.HttpStatusCodeNoContent,"appearance data not found");
+ }
+
+ rdata.initXmlWriter();
+
+ FormatUserAppearance(rdata);
+
+ // Indicate a successful request
+
+ rdata.Complete();
+
+ // Send the response to the user. The body will be implicitly
+ // constructed from the result of the XML writer.
+
+ rdata.Respond(String.Format("Appearance {0} Normal completion", rdata.method));
+ }
+
+ ///
+ /// POST adds NEW information to the user profile database.
+ /// This effectively resets the appearance before applying those
+ /// characteristics supplied in the request.
+ ///
+
+ private void DoExtend(AppearanceRequestData rdata)
+ {
+ bool created = false;
+ bool modified = false;
+ string newnode = String.Empty;
+
+ AvatarAppearance old = Rest.AvatarServices.GetUserAppearance(rdata.userProfile.ID);
+ rdata.userAppearance = new AvatarAppearance();
+
+ rdata.userAppearance.Owner = old.Owner;
+
+ if (GetUserAppearance(rdata))
+ {
+ modified = rdata.userAppearance != null;
+ created = !modified;
+ Rest.AvatarServices.UpdateUserAppearance(rdata.userProfile.ID, rdata.userAppearance);
+ }
+
+ if (created)
+ {
+ newnode = String.Format("{0} {1}", rdata.userProfile.FirstName,
+ rdata.userProfile.SurName);
+ // Must include a location header with a URI that identifies the new resource.
+ rdata.AddHeader(Rest.HttpHeaderLocation,String.Format("http://{0}{1}:{2}{3}{4}",
+ rdata.hostname,rdata.port,rdata.path,Rest.UrlPathSeparator, newnode));
+ rdata.Complete(Rest.HttpStatusCodeCreated);
+ }
+ else
+ {
+ if (modified)
+ {
+ rdata.Complete(Rest.HttpStatusCodeOK);
+ }
+ else
+ {
+ rdata.Complete(Rest.HttpStatusCodeNoContent);
+ }
+ }
+
+ rdata.Respond(String.Format("Appearance {0} : Normal completion", rdata.method));
+
+ }
+
+ ///
+ /// This updates the user's appearance. not all aspects need to be provided,
+ /// only those supplied will be changed.
+ ///
+
+ private void DoUpdate(AppearanceRequestData rdata)
+ {
+
+ bool created = false;
+ bool modified = false;
+
+
+ rdata.userAppearance = Rest.AvatarServices.GetUserAppearance(rdata.userProfile.ID);
+
+ if (GetUserAppearance(rdata))
+ {
+ modified = rdata.userAppearance != null;
+ created = !modified;
+ Rest.AvatarServices.UpdateUserAppearance(rdata.userProfile.ID, rdata.userAppearance);
+ }
+
+ if (created)
+ {
+ rdata.Complete(Rest.HttpStatusCodeCreated);
+ }
+ else
+ {
+ if (modified)
+ {
+ rdata.Complete(Rest.HttpStatusCodeOK);
+ }
+ else
+ {
+ rdata.Complete(Rest.HttpStatusCodeNoContent);
+ }
+ }
+
+ rdata.Respond(String.Format("Appearance {0} : Normal completion", rdata.method));
+
+ }
+
+ ///
+ /// Delete the specified user's appearance. This actually performs a reset
+ /// to the default avatar appearance, if the info is already there.
+ /// Existing ownership is preserved. All prior updates are lost and can not
+ /// be recovered.
+ ///
+
+ private void DoDelete(AppearanceRequestData rdata)
+ {
+
+ AvatarAppearance old = Rest.AvatarServices.GetUserAppearance(rdata.userProfile.ID);
+
+ if (old != null)
+ {
+ rdata.userAppearance = new AvatarAppearance();
+
+ rdata.userAppearance.Owner = old.Owner;
+
+ Rest.AvatarServices.UpdateUserAppearance(rdata.userProfile.ID, rdata.userAppearance);
+
+ rdata.Complete();
+ }
+ else
+ {
+ rdata.Complete(Rest.HttpStatusCodeNoContent);
+ }
+
+ rdata.Respond(String.Format("Appearance {0} : Normal completion", rdata.method));
+
+ }
+
+#endregion method-specific processing
+
+ private bool GetUserAppearance(AppearanceRequestData rdata)
+ {
+
+ XmlReader xml;
+ bool indata = false;
+
+ rdata.initXmlReader();
+ xml = rdata.reader;
+
+ while (xml.Read())
+ {
+ switch (xml.NodeType)
+ {
+ case XmlNodeType.Element :
+ switch (xml.Name)
+ {
+ case "Appearance" :
+ if (xml.MoveToAttribute("Height"))
+ {
+ rdata.userAppearance.AvatarHeight = (float) Convert.ToDouble(xml.Value);
+ indata = true;
+ }
+ break;
+ case "Body" :
+ if (xml.MoveToAttribute("Item"))
+ {
+ rdata.userAppearance.BodyItem = xml.Value;
+ indata = true;
+ }
+ if (xml.MoveToAttribute("Asset"))
+ {
+ rdata.userAppearance.BodyAsset = xml.Value;
+ indata = true;
+ }
+ break;
+ case "Skin" :
+ if (xml.MoveToAttribute("Item"))
+ {
+ rdata.userAppearance.SkinItem = xml.Value;
+ indata = true;
+ }
+ if (xml.MoveToAttribute("Asset"))
+ {
+ rdata.userAppearance.SkinAsset = xml.Value;
+ indata = true;
+ }
+ break;
+ case "Hair" :
+ if (xml.MoveToAttribute("Item"))
+ {
+ rdata.userAppearance.HairItem = xml.Value;
+ indata = true;
+ }
+ if (xml.MoveToAttribute("Asset"))
+ {
+ rdata.userAppearance.HairAsset = xml.Value;
+ indata = true;
+ }
+ break;
+ case "Eyes" :
+ if (xml.MoveToAttribute("Item"))
+ {
+ rdata.userAppearance.EyesItem = xml.Value;
+ indata = true;
+ }
+ if (xml.MoveToAttribute("Asset"))
+ {
+ rdata.userAppearance.EyesAsset = xml.Value;
+ indata = true;
+ }
+ break;
+ case "Shirt" :
+ if (xml.MoveToAttribute("Item"))
+ {
+ rdata.userAppearance.ShirtItem = xml.Value;
+ indata = true;
+ }
+ if (xml.MoveToAttribute("Asset"))
+ {
+ rdata.userAppearance.ShirtAsset = xml.Value;
+ indata = true;
+ }
+ break;
+ case "Pants" :
+ if (xml.MoveToAttribute("Item"))
+ {
+ rdata.userAppearance.PantsItem = xml.Value;
+ indata = true;
+ }
+ if (xml.MoveToAttribute("Asset"))
+ {
+ rdata.userAppearance.PantsAsset = xml.Value;
+ indata = true;
+ }
+ break;
+ case "Shoes" :
+ if (xml.MoveToAttribute("Item"))
+ {
+ rdata.userAppearance.ShoesItem = xml.Value;
+ indata = true;
+ }
+ if (xml.MoveToAttribute("Asset"))
+ {
+ rdata.userAppearance.ShoesAsset = xml.Value;
+ indata = true;
+ }
+ break;
+ case "Socks" :
+ if (xml.MoveToAttribute("Item"))
+ {
+ rdata.userAppearance.SocksItem = xml.Value;
+ indata = true;
+ }
+ if (xml.MoveToAttribute("Asset"))
+ {
+ rdata.userAppearance.SocksAsset = xml.Value;
+ indata = true;
+ }
+ break;
+ case "Jacket" :
+ if (xml.MoveToAttribute("Item"))
+ {
+ rdata.userAppearance.JacketItem = xml.Value;
+ indata = true;
+ }
+ if (xml.MoveToAttribute("Asset"))
+ {
+ rdata.userAppearance.JacketAsset = xml.Value;
+ indata = true;
+ }
+ break;
+ case "Gloves" :
+ if (xml.MoveToAttribute("Item"))
+ {
+ rdata.userAppearance.GlovesItem = xml.Value;
+ indata = true;
+ }
+ if (xml.MoveToAttribute("Asset"))
+ {
+ rdata.userAppearance.GlovesAsset = xml.Value;
+ indata = true;
+ }
+ break;
+ case "UnderShirt" :
+ if (xml.MoveToAttribute("Item"))
+ {
+ rdata.userAppearance.UnderShirtItem = xml.Value;
+ indata = true;
+ }
+ if (xml.MoveToAttribute("Asset"))
+ {
+ rdata.userAppearance.UnderShirtAsset = xml.Value;
+ indata = true;
+ }
+ break;
+ case "UnderPants" :
+ if (xml.MoveToAttribute("Item"))
+ {
+ rdata.userAppearance.UnderPantsItem = xml.Value;
+ indata = true;
+ }
+ if (xml.MoveToAttribute("Asset"))
+ {
+ rdata.userAppearance.UnderPantsAsset = xml.Value;
+ indata = true;
+ }
+ break;
+ case "Skirt" :
+ if (xml.MoveToAttribute("Item"))
+ {
+ rdata.userAppearance.SkirtItem = xml.Value;
+ indata = true;
+ }
+ if (xml.MoveToAttribute("Asset"))
+ {
+ rdata.userAppearance.SkirtAsset = xml.Value;
+ indata = true;
+ }
+ break;
+ case "Attachment" :
+ {
+
+ int ap;
+ UUID asset;
+ UUID item;
+
+ if (xml.MoveToAttribute("AtPoint"))
+ {
+ ap = Convert.ToInt32(xml.Value);
+ if (xml.MoveToAttribute("Asset"))
+ {
+ asset = new UUID(xml.Value);
+ if (xml.MoveToAttribute("Asset"))
+ {
+ item = new UUID(xml.Value);
+ rdata.userAppearance.SetAttachment(ap, item, asset);
+ indata = true;
+ }
+ }
+ }
+ }
+ break;
+ case "Texture" :
+ if (xml.MoveToAttribute("Default"))
+ {
+ rdata.userAppearance.Texture = new Primitive.TextureEntry(new UUID(xml.Value));
+ indata = true;
+ }
+ break;
+ case "Face" :
+ {
+ uint index;
+ if (xml.MoveToAttribute("Index"))
+ {
+ index = Convert.ToUInt32(xml.Value);
+ if (xml.MoveToAttribute("Id"))
+ {
+ rdata.userAppearance.Texture.CreateFace(index).TextureID = new UUID(xml.Value);
+ indata = true;
+ }
+ }
+ }
+ break;
+ case "VisualParameters" :
+ {
+ xml.ReadContentAsBase64(rdata.userAppearance.VisualParams,
+ 0,rdata.userAppearance.VisualParams.Length);
+ indata = true;
+ }
+ break;
+ }
+ break;
+ }
+ }
+
+ return indata;
+
+ }
+
+ private void FormatUserAppearance(AppearanceRequestData rdata)
+ {
+
+ rdata.writer.WriteStartElement("Appearance");
+ rdata.writer.WriteAttributeString("Height", rdata.userAppearance.AvatarHeight.ToString());
+
+ rdata.writer.WriteStartElement("Body");
+ rdata.writer.WriteAttributeString("Item",rdata.userAppearance.BodyItem.ToString());
+ rdata.writer.WriteAttributeString("Asset",rdata.userAppearance.BodyAsset.ToString());
+ rdata.writer.WriteEndElement();
+
+ rdata.writer.WriteStartElement("Skin");
+ rdata.writer.WriteAttributeString("Item",rdata.userAppearance.SkinItem.ToString());
+ rdata.writer.WriteAttributeString("Asset",rdata.userAppearance.SkinAsset.ToString());
+ rdata.writer.WriteEndElement();
+
+ rdata.writer.WriteStartElement("Hair");
+ rdata.writer.WriteAttributeString("Item",rdata.userAppearance.HairItem.ToString());
+ rdata.writer.WriteAttributeString("Asset",rdata.userAppearance.HairAsset.ToString());
+ rdata.writer.WriteEndElement();
+
+ rdata.writer.WriteStartElement("Eyes");
+ rdata.writer.WriteAttributeString("Item",rdata.userAppearance.EyesItem.ToString());
+ rdata.writer.WriteAttributeString("Asset",rdata.userAppearance.EyesAsset.ToString());
+ rdata.writer.WriteEndElement();
+
+ rdata.writer.WriteStartElement("Shirt");
+ rdata.writer.WriteAttributeString("Item",rdata.userAppearance.ShirtItem.ToString());
+ rdata.writer.WriteAttributeString("Asset",rdata.userAppearance.ShirtAsset.ToString());
+ rdata.writer.WriteEndElement();
+
+ rdata.writer.WriteStartElement("Pants");
+ rdata.writer.WriteAttributeString("Item",rdata.userAppearance.PantsItem.ToString());
+ rdata.writer.WriteAttributeString("Asset",rdata.userAppearance.PantsAsset.ToString());
+ rdata.writer.WriteEndElement();
+
+ rdata.writer.WriteStartElement("Shoes");
+ rdata.writer.WriteAttributeString("Item",rdata.userAppearance.ShoesItem.ToString());
+ rdata.writer.WriteAttributeString("Asset",rdata.userAppearance.ShoesAsset.ToString());
+ rdata.writer.WriteEndElement();
+
+ rdata.writer.WriteStartElement("Socks");
+ rdata.writer.WriteAttributeString("Item",rdata.userAppearance.SocksItem.ToString());
+ rdata.writer.WriteAttributeString("Asset",rdata.userAppearance.SocksAsset.ToString());
+ rdata.writer.WriteEndElement();
+
+ rdata.writer.WriteStartElement("Jacket");
+ rdata.writer.WriteAttributeString("Item",rdata.userAppearance.JacketItem.ToString());
+ rdata.writer.WriteAttributeString("Asset",rdata.userAppearance.JacketAsset.ToString());
+ rdata.writer.WriteEndElement();
+
+ rdata.writer.WriteStartElement("Gloves");
+ rdata.writer.WriteAttributeString("Item",rdata.userAppearance.GlovesItem.ToString());
+ rdata.writer.WriteAttributeString("Asset",rdata.userAppearance.GlovesAsset.ToString());
+ rdata.writer.WriteEndElement();
+
+ rdata.writer.WriteStartElement("UnderShirt");
+ rdata.writer.WriteAttributeString("Item",rdata.userAppearance.UnderShirtItem.ToString());
+ rdata.writer.WriteAttributeString("Asset",rdata.userAppearance.UnderShirtAsset.ToString());
+ rdata.writer.WriteEndElement();
+
+ rdata.writer.WriteStartElement("UnderPants");
+ rdata.writer.WriteAttributeString("Item",rdata.userAppearance.UnderPantsItem.ToString());
+ rdata.writer.WriteAttributeString("Asset",rdata.userAppearance.UnderPantsAsset.ToString());
+ rdata.writer.WriteEndElement();
+
+ rdata.writer.WriteStartElement("Skirt");
+ rdata.writer.WriteAttributeString("Item",rdata.userAppearance.SkirtItem.ToString());
+ rdata.writer.WriteAttributeString("Asset",rdata.userAppearance.SkirtAsset.ToString());
+ rdata.writer.WriteEndElement();
+
+ Hashtable attachments = rdata.userAppearance.GetAttachments();
+
+ rdata.writer.WriteStartElement("Attachments");
+
+ for (int i=0; i
+ /// These are the inventory specific request/response state
+ /// extensions.
+ ///
+
+ internal UUID uuid = UUID.Zero;
+ internal UserProfileData userProfile = null;
+ internal AvatarAppearance userAppearance = null;
+
+ internal AppearanceRequestData(OSHttpRequest request, OSHttpResponse response, string prefix)
+ : base(request, response, prefix)
+ {
+ }
+
+ }
+
+ #endregion Appearance RequestData extension
+
+ }
+}
diff --git a/OpenSim/ApplicationPlugins/Rest/rest.xsd b/OpenSim/ApplicationPlugins/Rest/rest.xsd
new file mode 100644
index 0000000..4dc0ae4
--- /dev/null
+++ b/OpenSim/ApplicationPlugins/Rest/rest.xsd
@@ -0,0 +1,276 @@
+
+
+
+
+ Open Simulator Export/Import XML schema
+ August 2008
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
--
cgit v1.1