aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--OpenSim/ApplicationPlugins/Rest/Inventory/RestAppearanceServices.cs826
-rw-r--r--OpenSim/ApplicationPlugins/Rest/rest.xsd276
2 files changed, 1102 insertions, 0 deletions
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 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSim Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections;
30using System.Collections.Generic;
31using System.IO;
32using System.Threading;
33using System.Xml;
34using System.Drawing;
35using OpenSim.Framework;
36using OpenSim.Framework.Servers;
37using OpenSim.Framework.Communications;
38using OpenSim.Framework.Communications.Cache;
39using OpenMetaverse;
40using OpenMetaverse.Imaging;
41using Nini.Config;
42
43namespace OpenSim.ApplicationPlugins.Rest.Inventory
44{
45 public class RestAppearanceServices : IRest
46 {
47 private static readonly int PARM_USERID = 0;
48 private static readonly int PARM_PATH = 1;
49
50 private bool enabled = false;
51 private string qPrefix = "appearance";
52
53 /// <summary>
54 /// The constructor makes sure that the service prefix is absolute
55 /// and the registers the service handler and the allocator.
56 /// </summary>
57
58 public RestAppearanceServices()
59 {
60 Rest.Log.InfoFormat("{0} User appearance services initializing", MsgId);
61 Rest.Log.InfoFormat("{0} Using REST Implementation Version {1}", MsgId, Rest.Version);
62
63 // If a relative path was specified for the handler's domain,
64 // add the standard prefix to make it absolute, e.g. /admin
65
66 if (!qPrefix.StartsWith(Rest.UrlPathSeparator))
67 {
68 Rest.Log.InfoFormat("{0} Domain is relative, adding absolute prefix", MsgId);
69 qPrefix = String.Format("{0}{1}{2}", Rest.Prefix, Rest.UrlPathSeparator, qPrefix);
70 Rest.Log.InfoFormat("{0} Domain is now <{1}>", MsgId, qPrefix);
71 }
72
73 // Register interface using the absolute URI.
74
75 Rest.Plugin.AddPathHandler(DoAppearance,qPrefix,Allocate);
76
77 // Activate if everything went OK
78
79 enabled = true;
80
81 Rest.Log.InfoFormat("{0} User appearance services initialization complete", MsgId);
82 }
83
84 /// <summary>
85 /// Post-construction, pre-enabled initialization opportunity
86 /// Not currently exploited.
87 /// </summary>
88
89 public void Initialize()
90 {
91 }
92
93 /// <summary>
94 /// Called by the plug-in to halt service processing. Local processing is
95 /// disabled.
96 /// </summary>
97
98 public void Close()
99 {
100 enabled = false;
101 Rest.Log.InfoFormat("{0} User appearance services closing down", MsgId);
102 }
103
104 /// <summary>
105 /// This property is declared locally because it is used a lot and
106 /// brevity is nice.
107 /// </summary>
108
109 internal string MsgId
110 {
111 get { return Rest.MsgId; }
112 }
113
114 #region Interface
115
116 /// <summary>
117 /// The plugin (RestHandler) calls this method to allocate the request
118 /// state carrier for a new request. It is destroyed when the request
119 /// completes. All request-instance specific state is kept here. This
120 /// is registered when this service provider is registered.
121 /// </summary>
122 /// <param name=request>Inbound HTTP request information</param>
123 /// <param name=response>Outbound HTTP request information</param>
124 /// <param name=qPrefix>REST service domain prefix</param>
125 /// <returns>A RequestData instance suitable for this service</returns>
126
127 private RequestData Allocate(OSHttpRequest request, OSHttpResponse response, string prefix)
128 {
129 return (RequestData) new AppearanceRequestData(request, response, prefix);
130 }
131
132 /// <summary>
133 /// This method is registered with the handler when this service provider
134 /// is initialized. It is called whenever the plug-in identifies this service
135 /// provider as the best match for a given request.
136 /// It handles all aspects of inventory REST processing, i.e. /admin/inventory
137 /// </summary>
138 /// <param name=hdata>A consolidated HTTP request work area</param>
139
140 private void DoAppearance(RequestData hdata)
141 {
142 AppearanceRequestData rdata = (AppearanceRequestData) hdata;
143
144 Rest.Log.DebugFormat("{0} DoAppearance ENTRY", MsgId);
145
146 // If we're disabled, do nothing.
147
148 if (!enabled)
149 {
150 return;
151 }
152
153 // Now that we know this is a serious attempt to
154 // access inventory data, we should find out who
155 // is asking, and make sure they are authorized
156 // to do so. We need to validate the caller's
157 // identity before revealing anything about the
158 // status quo. Authenticate throws an exception
159 // via Fail if no identity information is present.
160 //
161 // With the present HTTP server we can't use the
162 // builtin authentication mechanisms because they
163 // would be enforced for all in-bound requests.
164 // Instead we look at the headers ourselves and
165 // handle authentication directly.
166
167 try
168 {
169 if (!rdata.IsAuthenticated)
170 {
171 rdata.Fail(Rest.HttpStatusCodeNotAuthorized,String.Format("user \"{0}\" could not be authenticated", rdata.userName));
172 }
173 }
174 catch (RestException e)
175 {
176 if (e.statusCode == Rest.HttpStatusCodeNotAuthorized)
177 {
178 Rest.Log.WarnFormat("{0} User not authenticated", MsgId);
179 Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId, rdata.request.Headers.Get("Authorization"));
180 }
181 else
182 {
183 Rest.Log.ErrorFormat("{0} User authentication failed", MsgId);
184 Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId, rdata.request.Headers.Get("Authorization"));
185 }
186 throw (e);
187 }
188
189 Rest.Log.DebugFormat("{0} Authenticated {1}", MsgId, rdata.userName);
190
191 // We can only get here if we are authorized
192 //
193 // The requestor may have specified an UUID or
194 // a conjoined FirstName LastName string. We'll
195 // try both. If we fail with the first, UUID,
196 // attempt, we try the other. As an example, the
197 // URI for a valid inventory request might be:
198 //
199 // http://<host>:<port>/admin/inventory/Arthur Dent
200 //
201 // Indicating that this is an inventory request for
202 // an avatar named Arthur Dent. This is ALL that is
203 // required to designate a GET for an entire
204 // inventory.
205 //
206
207
208 // Do we have at least a user agent name?
209
210 if (rdata.Parameters.Length < 1)
211 {
212 Rest.Log.WarnFormat("{0} Appearance: No user agent identifier specified", MsgId);
213 rdata.Fail(Rest.HttpStatusCodeBadRequest, "no user identity specified");
214 }
215
216 // The first parameter MUST be the agent identification, either an UUID
217 // or a space-separated First-name Last-Name specification. We check for
218 // an UUID first, if anyone names their character using a valid UUID
219 // that identifies another existing avatar will cause this a problem...
220
221 try
222 {
223 rdata.uuid = new UUID(rdata.Parameters[PARM_USERID]);
224 Rest.Log.DebugFormat("{0} UUID supplied", MsgId);
225 rdata.userProfile = Rest.UserServices.GetUserProfile(rdata.uuid);
226 }
227 catch
228 {
229 string[] names = rdata.Parameters[PARM_USERID].Split(Rest.CA_SPACE);
230 if (names.Length == 2)
231 {
232 Rest.Log.DebugFormat("{0} Agent Name supplied [2]", MsgId);
233 rdata.userProfile = Rest.UserServices.GetUserProfile(names[0],names[1]);
234 }
235 else
236 {
237 Rest.Log.WarnFormat("{0} A Valid UUID or both first and last names must be specified", MsgId);
238 rdata.Fail(Rest.HttpStatusCodeBadRequest, "invalid user identity");
239 }
240 }
241
242 // If the user profile is null then either the server is broken, or the
243 // user is not known. We always assume the latter case.
244
245 if (rdata.userProfile != null)
246 {
247 Rest.Log.DebugFormat("{0} User profile obtained for agent {1} {2}",
248 MsgId, rdata.userProfile.FirstName, rdata.userProfile.SurName);
249 }
250 else
251 {
252 Rest.Log.WarnFormat("{0} No user profile for {1}", MsgId, rdata.path);
253 rdata.Fail(Rest.HttpStatusCodeNotFound, "unrecognized user identity");
254 }
255
256 // If we get to here, then we have effectively validated the user's
257
258 switch (rdata.method)
259 {
260 case Rest.HEAD : // Do the processing, set the status code, suppress entity
261 DoGet(rdata);
262 rdata.buffer = null;
263 break;
264
265 case Rest.GET : // Do the processing, set the status code, return entity
266 DoGet(rdata);
267 break;
268
269 case Rest.PUT : // Update named element
270 DoUpdate(rdata);
271 break;
272
273 case Rest.POST : // Add new information to identified context.
274 DoExtend(rdata);
275 break;
276
277 case Rest.DELETE : // Delete information
278 DoDelete(rdata);
279 break;
280
281 default :
282 Rest.Log.WarnFormat("{0} Method {1} not supported for {2}",
283 MsgId, rdata.method, rdata.path);
284 rdata.Fail(Rest.HttpStatusCodeMethodNotAllowed,
285 String.Format("{0} not supported", rdata.method));
286 break;
287 }
288 }
289
290 #endregion Interface
291
292 #region method-specific processing
293
294 /// <summary>
295 /// This method implements GET processing for user's appearance.
296 /// </summary>
297 /// <param name=rdata>HTTP service request work area</param>
298
299 private void DoGet(AppearanceRequestData rdata)
300 {
301
302 rdata.userAppearance = Rest.AvatarServices.GetUserAppearance(rdata.userProfile.ID);
303
304 if (rdata.userAppearance == null)
305 {
306 rdata.Fail(Rest.HttpStatusCodeNoContent,"appearance data not found");
307 }
308
309 rdata.initXmlWriter();
310
311 FormatUserAppearance(rdata);
312
313 // Indicate a successful request
314
315 rdata.Complete();
316
317 // Send the response to the user. The body will be implicitly
318 // constructed from the result of the XML writer.
319
320 rdata.Respond(String.Format("Appearance {0} Normal completion", rdata.method));
321 }
322
323 /// <summary>
324 /// POST adds NEW information to the user profile database.
325 /// This effectively resets the appearance before applying those
326 /// characteristics supplied in the request.
327 /// </summary>
328
329 private void DoExtend(AppearanceRequestData rdata)
330 {
331 bool created = false;
332 bool modified = false;
333 string newnode = String.Empty;
334
335 AvatarAppearance old = Rest.AvatarServices.GetUserAppearance(rdata.userProfile.ID);
336 rdata.userAppearance = new AvatarAppearance();
337
338 rdata.userAppearance.Owner = old.Owner;
339
340 if (GetUserAppearance(rdata))
341 {
342 modified = rdata.userAppearance != null;
343 created = !modified;
344 Rest.AvatarServices.UpdateUserAppearance(rdata.userProfile.ID, rdata.userAppearance);
345 }
346
347 if (created)
348 {
349 newnode = String.Format("{0} {1}", rdata.userProfile.FirstName,
350 rdata.userProfile.SurName);
351 // Must include a location header with a URI that identifies the new resource.
352 rdata.AddHeader(Rest.HttpHeaderLocation,String.Format("http://{0}{1}:{2}{3}{4}",
353 rdata.hostname,rdata.port,rdata.path,Rest.UrlPathSeparator, newnode));
354 rdata.Complete(Rest.HttpStatusCodeCreated);
355 }
356 else
357 {
358 if (modified)
359 {
360 rdata.Complete(Rest.HttpStatusCodeOK);
361 }
362 else
363 {
364 rdata.Complete(Rest.HttpStatusCodeNoContent);
365 }
366 }
367
368 rdata.Respond(String.Format("Appearance {0} : Normal completion", rdata.method));
369
370 }
371
372 /// <summary>
373 /// This updates the user's appearance. not all aspects need to be provided,
374 /// only those supplied will be changed.
375 /// </summary>
376
377 private void DoUpdate(AppearanceRequestData rdata)
378 {
379
380 bool created = false;
381 bool modified = false;
382
383
384 rdata.userAppearance = Rest.AvatarServices.GetUserAppearance(rdata.userProfile.ID);
385
386 if (GetUserAppearance(rdata))
387 {
388 modified = rdata.userAppearance != null;
389 created = !modified;
390 Rest.AvatarServices.UpdateUserAppearance(rdata.userProfile.ID, rdata.userAppearance);
391 }
392
393 if (created)
394 {
395 rdata.Complete(Rest.HttpStatusCodeCreated);
396 }
397 else
398 {
399 if (modified)
400 {
401 rdata.Complete(Rest.HttpStatusCodeOK);
402 }
403 else
404 {
405 rdata.Complete(Rest.HttpStatusCodeNoContent);
406 }
407 }
408
409 rdata.Respond(String.Format("Appearance {0} : Normal completion", rdata.method));
410
411 }
412
413 /// <summary>
414 /// Delete the specified user's appearance. This actually performs a reset
415 /// to the default avatar appearance, if the info is already there.
416 /// Existing ownership is preserved. All prior updates are lost and can not
417 /// be recovered.
418 /// </summary>
419
420 private void DoDelete(AppearanceRequestData rdata)
421 {
422
423 AvatarAppearance old = Rest.AvatarServices.GetUserAppearance(rdata.userProfile.ID);
424
425 if (old != null)
426 {
427 rdata.userAppearance = new AvatarAppearance();
428
429 rdata.userAppearance.Owner = old.Owner;
430
431 Rest.AvatarServices.UpdateUserAppearance(rdata.userProfile.ID, rdata.userAppearance);
432
433 rdata.Complete();
434 }
435 else
436 {
437 rdata.Complete(Rest.HttpStatusCodeNoContent);
438 }
439
440 rdata.Respond(String.Format("Appearance {0} : Normal completion", rdata.method));
441
442 }
443
444#endregion method-specific processing
445
446 private bool GetUserAppearance(AppearanceRequestData rdata)
447 {
448
449 XmlReader xml;
450 bool indata = false;
451
452 rdata.initXmlReader();
453 xml = rdata.reader;
454
455 while (xml.Read())
456 {
457 switch (xml.NodeType)
458 {
459 case XmlNodeType.Element :
460 switch (xml.Name)
461 {
462 case "Appearance" :
463 if (xml.MoveToAttribute("Height"))
464 {
465 rdata.userAppearance.AvatarHeight = (float) Convert.ToDouble(xml.Value);
466 indata = true;
467 }
468 break;
469 case "Body" :
470 if (xml.MoveToAttribute("Item"))
471 {
472 rdata.userAppearance.BodyItem = xml.Value;
473 indata = true;
474 }
475 if (xml.MoveToAttribute("Asset"))
476 {
477 rdata.userAppearance.BodyAsset = xml.Value;
478 indata = true;
479 }
480 break;
481 case "Skin" :
482 if (xml.MoveToAttribute("Item"))
483 {
484 rdata.userAppearance.SkinItem = xml.Value;
485 indata = true;
486 }
487 if (xml.MoveToAttribute("Asset"))
488 {
489 rdata.userAppearance.SkinAsset = xml.Value;
490 indata = true;
491 }
492 break;
493 case "Hair" :
494 if (xml.MoveToAttribute("Item"))
495 {
496 rdata.userAppearance.HairItem = xml.Value;
497 indata = true;
498 }
499 if (xml.MoveToAttribute("Asset"))
500 {
501 rdata.userAppearance.HairAsset = xml.Value;
502 indata = true;
503 }
504 break;
505 case "Eyes" :
506 if (xml.MoveToAttribute("Item"))
507 {
508 rdata.userAppearance.EyesItem = xml.Value;
509 indata = true;
510 }
511 if (xml.MoveToAttribute("Asset"))
512 {
513 rdata.userAppearance.EyesAsset = xml.Value;
514 indata = true;
515 }
516 break;
517 case "Shirt" :
518 if (xml.MoveToAttribute("Item"))
519 {
520 rdata.userAppearance.ShirtItem = xml.Value;
521 indata = true;
522 }
523 if (xml.MoveToAttribute("Asset"))
524 {
525 rdata.userAppearance.ShirtAsset = xml.Value;
526 indata = true;
527 }
528 break;
529 case "Pants" :
530 if (xml.MoveToAttribute("Item"))
531 {
532 rdata.userAppearance.PantsItem = xml.Value;
533 indata = true;
534 }
535 if (xml.MoveToAttribute("Asset"))
536 {
537 rdata.userAppearance.PantsAsset = xml.Value;
538 indata = true;
539 }
540 break;
541 case "Shoes" :
542 if (xml.MoveToAttribute("Item"))
543 {
544 rdata.userAppearance.ShoesItem = xml.Value;
545 indata = true;
546 }
547 if (xml.MoveToAttribute("Asset"))
548 {
549 rdata.userAppearance.ShoesAsset = xml.Value;
550 indata = true;
551 }
552 break;
553 case "Socks" :
554 if (xml.MoveToAttribute("Item"))
555 {
556 rdata.userAppearance.SocksItem = xml.Value;
557 indata = true;
558 }
559 if (xml.MoveToAttribute("Asset"))
560 {
561 rdata.userAppearance.SocksAsset = xml.Value;
562 indata = true;
563 }
564 break;
565 case "Jacket" :
566 if (xml.MoveToAttribute("Item"))
567 {
568 rdata.userAppearance.JacketItem = xml.Value;
569 indata = true;
570 }
571 if (xml.MoveToAttribute("Asset"))
572 {
573 rdata.userAppearance.JacketAsset = xml.Value;
574 indata = true;
575 }
576 break;
577 case "Gloves" :
578 if (xml.MoveToAttribute("Item"))
579 {
580 rdata.userAppearance.GlovesItem = xml.Value;
581 indata = true;
582 }
583 if (xml.MoveToAttribute("Asset"))
584 {
585 rdata.userAppearance.GlovesAsset = xml.Value;
586 indata = true;
587 }
588 break;
589 case "UnderShirt" :
590 if (xml.MoveToAttribute("Item"))
591 {
592 rdata.userAppearance.UnderShirtItem = xml.Value;
593 indata = true;
594 }
595 if (xml.MoveToAttribute("Asset"))
596 {
597 rdata.userAppearance.UnderShirtAsset = xml.Value;
598 indata = true;
599 }
600 break;
601 case "UnderPants" :
602 if (xml.MoveToAttribute("Item"))
603 {
604 rdata.userAppearance.UnderPantsItem = xml.Value;
605 indata = true;
606 }
607 if (xml.MoveToAttribute("Asset"))
608 {
609 rdata.userAppearance.UnderPantsAsset = xml.Value;
610 indata = true;
611 }
612 break;
613 case "Skirt" :
614 if (xml.MoveToAttribute("Item"))
615 {
616 rdata.userAppearance.SkirtItem = xml.Value;
617 indata = true;
618 }
619 if (xml.MoveToAttribute("Asset"))
620 {
621 rdata.userAppearance.SkirtAsset = xml.Value;
622 indata = true;
623 }
624 break;
625 case "Attachment" :
626 {
627
628 int ap;
629 UUID asset;
630 UUID item;
631
632 if (xml.MoveToAttribute("AtPoint"))
633 {
634 ap = Convert.ToInt32(xml.Value);
635 if (xml.MoveToAttribute("Asset"))
636 {
637 asset = new UUID(xml.Value);
638 if (xml.MoveToAttribute("Asset"))
639 {
640 item = new UUID(xml.Value);
641 rdata.userAppearance.SetAttachment(ap, item, asset);
642 indata = true;
643 }
644 }
645 }
646 }
647 break;
648 case "Texture" :
649 if (xml.MoveToAttribute("Default"))
650 {
651 rdata.userAppearance.Texture = new Primitive.TextureEntry(new UUID(xml.Value));
652 indata = true;
653 }
654 break;
655 case "Face" :
656 {
657 uint index;
658 if (xml.MoveToAttribute("Index"))
659 {
660 index = Convert.ToUInt32(xml.Value);
661 if (xml.MoveToAttribute("Id"))
662 {
663 rdata.userAppearance.Texture.CreateFace(index).TextureID = new UUID(xml.Value);
664 indata = true;
665 }
666 }
667 }
668 break;
669 case "VisualParameters" :
670 {
671 xml.ReadContentAsBase64(rdata.userAppearance.VisualParams,
672 0,rdata.userAppearance.VisualParams.Length);
673 indata = true;
674 }
675 break;
676 }
677 break;
678 }
679 }
680
681 return indata;
682
683 }
684
685 private void FormatUserAppearance(AppearanceRequestData rdata)
686 {
687
688 rdata.writer.WriteStartElement("Appearance");
689 rdata.writer.WriteAttributeString("Height", rdata.userAppearance.AvatarHeight.ToString());
690
691 rdata.writer.WriteStartElement("Body");
692 rdata.writer.WriteAttributeString("Item",rdata.userAppearance.BodyItem.ToString());
693 rdata.writer.WriteAttributeString("Asset",rdata.userAppearance.BodyAsset.ToString());
694 rdata.writer.WriteEndElement();
695
696 rdata.writer.WriteStartElement("Skin");
697 rdata.writer.WriteAttributeString("Item",rdata.userAppearance.SkinItem.ToString());
698 rdata.writer.WriteAttributeString("Asset",rdata.userAppearance.SkinAsset.ToString());
699 rdata.writer.WriteEndElement();
700
701 rdata.writer.WriteStartElement("Hair");
702 rdata.writer.WriteAttributeString("Item",rdata.userAppearance.HairItem.ToString());
703 rdata.writer.WriteAttributeString("Asset",rdata.userAppearance.HairAsset.ToString());
704 rdata.writer.WriteEndElement();
705
706 rdata.writer.WriteStartElement("Eyes");
707 rdata.writer.WriteAttributeString("Item",rdata.userAppearance.EyesItem.ToString());
708 rdata.writer.WriteAttributeString("Asset",rdata.userAppearance.EyesAsset.ToString());
709 rdata.writer.WriteEndElement();
710
711 rdata.writer.WriteStartElement("Shirt");
712 rdata.writer.WriteAttributeString("Item",rdata.userAppearance.ShirtItem.ToString());
713 rdata.writer.WriteAttributeString("Asset",rdata.userAppearance.ShirtAsset.ToString());
714 rdata.writer.WriteEndElement();
715
716 rdata.writer.WriteStartElement("Pants");
717 rdata.writer.WriteAttributeString("Item",rdata.userAppearance.PantsItem.ToString());
718 rdata.writer.WriteAttributeString("Asset",rdata.userAppearance.PantsAsset.ToString());
719 rdata.writer.WriteEndElement();
720
721 rdata.writer.WriteStartElement("Shoes");
722 rdata.writer.WriteAttributeString("Item",rdata.userAppearance.ShoesItem.ToString());
723 rdata.writer.WriteAttributeString("Asset",rdata.userAppearance.ShoesAsset.ToString());
724 rdata.writer.WriteEndElement();
725
726 rdata.writer.WriteStartElement("Socks");
727 rdata.writer.WriteAttributeString("Item",rdata.userAppearance.SocksItem.ToString());
728 rdata.writer.WriteAttributeString("Asset",rdata.userAppearance.SocksAsset.ToString());
729 rdata.writer.WriteEndElement();
730
731 rdata.writer.WriteStartElement("Jacket");
732 rdata.writer.WriteAttributeString("Item",rdata.userAppearance.JacketItem.ToString());
733 rdata.writer.WriteAttributeString("Asset",rdata.userAppearance.JacketAsset.ToString());
734 rdata.writer.WriteEndElement();
735
736 rdata.writer.WriteStartElement("Gloves");
737 rdata.writer.WriteAttributeString("Item",rdata.userAppearance.GlovesItem.ToString());
738 rdata.writer.WriteAttributeString("Asset",rdata.userAppearance.GlovesAsset.ToString());
739 rdata.writer.WriteEndElement();
740
741 rdata.writer.WriteStartElement("UnderShirt");
742 rdata.writer.WriteAttributeString("Item",rdata.userAppearance.UnderShirtItem.ToString());
743 rdata.writer.WriteAttributeString("Asset",rdata.userAppearance.UnderShirtAsset.ToString());
744 rdata.writer.WriteEndElement();
745
746 rdata.writer.WriteStartElement("UnderPants");
747 rdata.writer.WriteAttributeString("Item",rdata.userAppearance.UnderPantsItem.ToString());
748 rdata.writer.WriteAttributeString("Asset",rdata.userAppearance.UnderPantsAsset.ToString());
749 rdata.writer.WriteEndElement();
750
751 rdata.writer.WriteStartElement("Skirt");
752 rdata.writer.WriteAttributeString("Item",rdata.userAppearance.SkirtItem.ToString());
753 rdata.writer.WriteAttributeString("Asset",rdata.userAppearance.SkirtAsset.ToString());
754 rdata.writer.WriteEndElement();
755
756 Hashtable attachments = rdata.userAppearance.GetAttachments();
757
758 rdata.writer.WriteStartElement("Attachments");
759
760 for (int i=0; i<attachments.Count;i++)
761 {
762 Hashtable attachment = attachments[i] as Hashtable;
763 rdata.writer.WriteStartElement("Attachment");
764 rdata.writer.WriteAttributeString("AtPoint", i.ToString());
765 rdata.writer.WriteAttributeString("Item", (string) attachment["item"]);
766 rdata.writer.WriteAttributeString("Asset", (string) attachment["asset"]);
767 rdata.writer.WriteEndElement();
768 }
769
770 rdata.writer.WriteEndElement();
771
772
773 Primitive.TextureEntry texture = rdata.userAppearance.Texture;
774
775 if (texture != null)
776 {
777 rdata.writer.WriteStartElement("Texture");
778 rdata.writer.WriteAttributeString("Default",
779 rdata.userAppearance.Texture.DefaultTexture.TextureID.ToString());
780
781 for (int i=0; i<Primitive.TextureEntry.MAX_FACES;i++)
782 {
783 rdata.writer.WriteStartElement("Face");
784 rdata.writer.WriteAttributeString("Index", i.ToString());
785 rdata.writer.WriteAttributeString("Id",
786 rdata.userAppearance.Texture.FaceTextures[i].TextureID.ToString());
787 rdata.writer.WriteEndElement();
788 }
789
790 rdata.writer.WriteEndElement();
791 }
792
793 rdata.writer.WriteStartElement("VisualParameters");
794 rdata.writer.WriteBase64(rdata.userAppearance.VisualParams,0,
795 rdata.userAppearance.VisualParams.Length);
796 rdata.writer.WriteEndElement();
797 rdata.writer.WriteFullEndElement();
798
799 return;
800 }
801
802 #region appearance RequestData extension
803
804 internal class AppearanceRequestData : RequestData
805 {
806
807 /// <summary>
808 /// These are the inventory specific request/response state
809 /// extensions.
810 /// </summary>
811
812 internal UUID uuid = UUID.Zero;
813 internal UserProfileData userProfile = null;
814 internal AvatarAppearance userAppearance = null;
815
816 internal AppearanceRequestData(OSHttpRequest request, OSHttpResponse response, string prefix)
817 : base(request, response, prefix)
818 {
819 }
820
821 }
822
823 #endregion Appearance RequestData extension
824
825 }
826}
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 @@
1<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
2
3 <xsd:annotation>
4 <xsd:documentation xml:lang="en">
5 Open Simulator Export/Import XML schema
6 August 2008
7 </xsd:documentation>
8 </xsd:annotation>
9
10 <!-- WARNING!!!
11 This is currently a draft, it does not reflect
12 what is exported, nor what will be understood
13 on import. It is included as a working document
14 and this comment will be removed at such time as
15 the schema corresponds to reality.
16 -->
17
18 <!--
19 REST-related information
20 Inventory data is always framed by an
21 inventory element. Consists of zero or
22 more elements representing either folders
23 or items within those folders. The inventory
24 element represents the "real" root folder.
25 -->
26
27 <xsd:element name="inventory" type="inventory_ct" />
28
29 <!--
30 The inventory complex type is just an arbitrary
31 sequence of folders and items. In reality it is
32 typically just folders. Both item and folder
33 have corresponding complex types. It is distinct
34 from folders insofar as it has no other defining
35 attributes.
36 -->
37
38 <xsd:complexType name="inventory_ct">
39 <xsd:element name="folder" type="folder_ct" maxOccurs="unbounded"/>
40 <xsd:element name="item" type="item_ct" maxOccurs="unbounded" />
41 </xsd:complexType>
42
43 <xsd:complexType name="folder_ct">
44 <xsd:attribute name="UUID" type="uuid_st" />
45 <xsd:attribute name="name" type="name_st" />
46 <xsd:attribute name="type" type="folder_type_st" />
47 <xsd:attribute name="description" type="xsd:string" /> <!-- added -->
48 <xsd:attribute name="version" type="unsignedShort" />
49 <xsd:attribute name="owner" type="uuid_st" />
50
51 <xsd:attribute name="creator" type="uuid_st" /> <!-- added -->
52 <xsd:attribute name="creationdate" type="date_st" /> <!-- added -->
53
54 <xsd:attribute name="parent" type="uuid_st" />
55
56 <xsd:element name="permissions" type="permissions_ct" maxOccurs="unbounded" /> <!-- added -->
57 <xsd:element name="folder" type="folder_ct" maxOccurs="unbounded" />
58 <xsd:element name="item" type="item_ct" maxOccurs="unbounded" />
59 </xsd:complexType>
60
61 <xsd:complexType name="item_ct">
62 <xsd:attribute name="UUID" type="uuid_st" />
63 <xsd:attribute name="name" type="name_st" />
64 <xsd:attribute name="type" type="inventory_type_st" />
65 <xsd:attribute name="description" type="xsd:string" />
66 <xsd:attribute name="version" type="unsignedShort" /> <!-- added -->
67 <xsd:attribute name="owner" type="uuid_st" />
68
69 <xsd:attribute name="creator" type="uuid_st" />
70 <xsd:attribute name="creationdate" type="date_st" />
71
72 <xsd:attribute name="folder" type="uuid_st" />
73 <xsd:attribute name="groupid" type="uuid_st" />
74 <xsd:attribute name="groupowned" type="xsd:boolean" />
75 <xsd:attribute name="saletype" type="sale_st" />
76 <xsd:attribute name="saleprice" type="xsd:decimal" />
77
78 <xsd:element name="permissions" type="permissions_ct" maxOccurs="unbounded" />
79 </xsd:complexType>
80
81 <xsd:complexType name="asset_ct">
82 <xsd:attribute name="UUID" type="uuid_st" />
83 <xsd:attribute name="name" type="name_st" />
84 <xsd:attribute name="type" type="asset_type_st" />
85 <xsd:attribute name="description" type="xsd:string" />
86 <xsd:attribute name="version" type="unsignedShort" /> <!-- added -->
87 <xsd:attribute name="owner" type="uuid_st" />
88
89 <xsd:attribute name="creator" type="uuid_st" />
90 <xsd:attribute name="creationdate" type="date_st" />
91
92 <xsd:attribute name="temporary" type="xsd:boolean" />
93 <xsd:attribute name="local" type="xsd:boolean" />
94 <xsd:attribute name="inline" type="xsd:boolean" />
95 </xsd:complexType>
96
97 <!-- Constrained Simple Data types -->
98
99 <!--
100 We need to specify name as a simple type because on
101 some platforms it is constrained by a certain length
102 limitation. For completeness we indicate that whitespace
103 should be preserved exactly as specified.
104 -->
105
106 <xsd:simpleType name="name_st">
107 <xsd:restriction base="xsd:string">
108 <whiteSpace value="preserve" />
109 <minLength value="0" />
110 <maxLength value="64" />
111 </xsd:restriction>
112 </xsd:simpleType>
113
114 <!--
115 Type information in the folder is meant to indicate
116 the preferred asset type for this folder. As such, that
117 currently corresponds to the type values allowed for
118 assets, however that is not mandated, so for
119 now at least I'll represent this as a distinct
120 enumeration.
121 This seems inappropriate; it seems like the folder's
122 content should reflect the InventoryType classifications
123 rather than the asset types.
124 -->
125
126 <xsd:simpleType name="folder_type_st">
127 <xsd:restriction base="xsd:string">
128 <xsd:enumeration value="Texture" />
129 <xsd:enumeration value="Sound" />
130 <xsd:enumeration value="CallingCard" />
131 <xsd:enumeration value="Landmark" />
132 <xsd:enumeration value="Script" />
133 <xsd:enumeration value="Clothing" />
134 <xsd:enumeration value="Object" />
135 <xsd:enumeration value="Notecard" />
136 <xsd:enumeration value="LSLText" />
137 <xsd:enumeration value="LSLByteCode" />
138 <xsd:enumeration value="TextureTGA" />
139 <xsd:enumeration value="BodyPart" />
140 <xsd:enumeration value="SoundWAV" />
141 <xsd:enumeration value="ImageTGA" />
142 <xsd:enumeration value="ImageJPEG" />
143 <xsd:enumeration value="Animation" />
144 <xsd:enumeration value="Gesture" />
145 <xsd:enumeration value="Simstate" />
146 <xsd:enumeration value="Unknown" />
147 <xsd:enumeration value="LostAndFoundFolder" />
148 <xsd:enumeration value="SnapshotFolder" />
149 <xsd:enumeration value="TrashFolder" />
150 <xsd:enumeration value="Folder" />
151 <xsd:enumeration value="RootFolder" />
152 </xsd:restriction>
153 </xsd:simpleType>
154
155 <!--
156 Inventory item type designates an asset class, rather
157 than a specific asset type. For example, "SnapShot"
158 might include a number of asset types such as JPEG,
159 TGA, etc.. This is not a consistent interpretation,
160 classifications such as LostAndFound are meta-types
161 relative to asset classes.
162
163 These types should be abstract and not be tied to a
164 specific platform. A world's import facility should be
165 responsible for mapping these to meaningful internal
166 representations.
167
168 These types were based on information in:
169 libsecondlife/InventoryManager.cs
170 -->
171
172 <xsd:simpleType name="inventory_type_st">
173 <xsd:restriction base="xsd:string">
174 <xsd:enumeration value="Texture" />
175 <xsd:enumeration value="Sound" />
176 <xsd:enumeration value="CallingCard" />
177 <xsd:enumeration value="Landmark" />
178 <xsd:enumeration value="Script" />
179 <xsd:enumeration value="Clothing" />
180 <xsd:enumeration value="Object" />
181 <xsd:enumeration value="Notecard" />
182 <xsd:enumeration value="LSL" />
183 <xsd:enumeration value="LSLBytecode" />
184 <xsd:enumeration value="TextureTGA" />
185 <xsd:enumeration value="BodyPart" />
186 <xsd:enumeration value="Snapshot" />
187 <xsd:enumeration value="Attachment" />
188 <xsd:enumeration value="Wearable" />
189 <xsd:enumeration value="Animation" />
190 <xsd:enumeration value="Gesture" />
191 <xsd:enumeration value="Folder" />
192 <xsd:enumeration value="Unknown" />
193 <xsd:enumeration value="LostAndFound" />
194 <xsd:enumeration value="Trash" />
195 <xsd:enumeration value="Root" />
196 </xsd:restriction>
197 </xsd:simpleType>
198
199 <!--
200 The asset types seem to be even more disarrayed than
201 the inventory types. It seems to be little more than
202 a reiteration of the inventory type information,
203 which adds little or nothing to the overall data
204 model.
205
206 Of course, given that these are drawn from the
207 libsecondlife definitions, we aren't at liberty to
208 simply redefine them in place. But the XML definitions
209 here could be made more useful.
210
211 These types were based on information in:
212 libsecondlife/AssetManager.cs
213 -->
214
215 <xsd:simpleType name="asset_type_st">
216 <xsd:restriction base="xsd:string">
217 <xsd:enumeration value="Texture" />
218 <xsd:enumeration value="Sound" />
219 <xsd:enumeration value="CallingCard" />
220 <xsd:enumeration value="Landmark" />
221 <xsd:enumeration value="Script" />
222 <xsd:enumeration value="Clothing" />
223 <xsd:enumeration value="Object" />
224 <xsd:enumeration value="Notecard" />
225 <xsd:enumeration value="LSLText" />
226 <xsd:enumeration value="LSLByteCode" />
227 <xsd:enumeration value="TextureTGA" />
228 <xsd:enumeration value="BodyPart" />
229 <xsd:enumeration value="SoundWAV" />
230 <xsd:enumeration value="ImageTGA" />
231 <xsd:enumeration value="ImageJPEG" />
232 <xsd:enumeration value="Animation" />
233 <xsd:enumeration value="Gesture" />
234 <xsd:enumeration value="Simstate" />
235 <xsd:enumeration value="Unknown" />
236 <xsd:enumeration value="LostAndFoundFolder" />
237 <xsd:enumeration value="SnapshotFolder" />
238 <xsd:enumeration value="TrashFolder" />
239 <xsd:enumeration value="Folder" />
240 <xsd:enumeration value="RootFolder" />
241 </xsd:restriction>
242 </xsd:simpleType>
243
244 <!-- This is describing the apparent form of a UUID. If
245 we ever want a more metaphysical definition we'll
246 need to add to it.
247 -->
248
249 <xsd:simpleType name="uuid_st">
250 <xsd:restriction base="xsd:string">
251 <xsd:pattern value="[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}"/>
252 </xsd:restriction>
253 </xsd:simpleType>
254
255 <!-- This constrains the date representation. Currently
256 it is simply an integer representing the elapsed
257 ?? since ??.
258 -->
259
260 <xsd:simpleType name="date_st">
261 <xsd:restriction base="xsd:positiveInteger">
262 </xsd:restriction>
263 </xsd:simpleType>
264
265 <!-- This constrains the representation of sale price.
266 Currently it is a simple decimal with no unit
267 specified.
268 Issues: interoperability.
269 -->
270
271 <xsd:simpleType name="sale_st">
272 <xsd:restriction base="xsd:decimal">
273 </xsd:restriction>
274 </xsd:simpleType>
275
276</xsd:schema>