aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/ApplicationPlugins/Rest/Inventory
diff options
context:
space:
mode:
authorDr Scofield2008-09-18 15:50:52 +0000
committerDr Scofield2008-09-18 15:50:52 +0000
commiteeb5381bbbf58190824d047d845569564368c708 (patch)
treec2bf745ae3246339c0bfaf7e0be3f5144c8806a6 /OpenSim/ApplicationPlugins/Rest/Inventory
parentAdds REST interface for setting avatar appearance. cleans up a couple (diff)
downloadopensim-SC-eeb5381bbbf58190824d047d845569564368c708.zip
opensim-SC-eeb5381bbbf58190824d047d845569564368c708.tar.gz
opensim-SC-eeb5381bbbf58190824d047d845569564368c708.tar.bz2
opensim-SC-eeb5381bbbf58190824d047d845569564368c708.tar.xz
and this actually adds the appearance code itself (and not just the
check-in message)
Diffstat (limited to 'OpenSim/ApplicationPlugins/Rest/Inventory')
-rw-r--r--OpenSim/ApplicationPlugins/Rest/Inventory/RestAppearanceServices.cs826
1 files changed, 826 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}