RestAppearanceServices.cs 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874
  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. using System;
  28. using System.Collections;
  29. using System.Collections.Generic;
  30. using System.IO;
  31. using System.Threading;
  32. using System.Xml;
  33. using System.Drawing;
  34. using OpenSim.Framework;
  35. using OpenSim.Framework.Servers;
  36. using OpenSim.Framework.Communications;
  37. using OpenSim.Framework.Communications.Cache;
  38. using OpenMetaverse;
  39. using OpenMetaverse.Imaging;
  40. using Nini.Config;
  41. namespace OpenSim.ApplicationPlugins.Rest.Inventory
  42. {
  43. public class RestAppearanceServices : IRest
  44. {
  45. private static readonly int PARM_USERID = 0;
  46. // private static readonly int PARM_PATH = 1;
  47. private bool enabled = false;
  48. private string qPrefix = "appearance";
  49. /// <summary>
  50. /// The constructor makes sure that the service prefix is absolute
  51. /// and the registers the service handler and the allocator.
  52. /// </summary>
  53. public RestAppearanceServices()
  54. {
  55. Rest.Log.InfoFormat("{0} User appearance services initializing", MsgId);
  56. Rest.Log.InfoFormat("{0} Using REST Implementation Version {1}", MsgId, Rest.Version);
  57. // This is better than a null reference.
  58. if (Rest.AvatarServices == null)
  59. throw new Exception(String.Format("{0} OpenSim inventory services are not available",
  60. MsgId));
  61. if (Rest.UserServices == null)
  62. throw new Exception(String.Format("{0} OpenSim user profile services are not available",
  63. MsgId));
  64. // If a relative path was specified for the handler's domain,
  65. // add the standard prefix to make it absolute, e.g. /admin
  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. // Register interface using the absolute URI.
  73. Rest.Plugin.AddPathHandler(DoAppearance,qPrefix,Allocate);
  74. // Activate if everything went OK
  75. enabled = true;
  76. Rest.Log.InfoFormat("{0} User appearance services initialization complete", MsgId);
  77. }
  78. /// <summary>
  79. /// Post-construction, pre-enabled initialization opportunity
  80. /// Not currently exploited.
  81. /// </summary>
  82. public void Initialize()
  83. {
  84. }
  85. /// <summary>
  86. /// Called by the plug-in to halt service processing. Local processing is
  87. /// disabled.
  88. /// </summary>
  89. public void Close()
  90. {
  91. enabled = false;
  92. Rest.Log.InfoFormat("{0} User appearance services closing down", MsgId);
  93. }
  94. /// <summary>
  95. /// This property is declared locally because it is used a lot and
  96. /// brevity is nice.
  97. /// </summary>
  98. internal string MsgId
  99. {
  100. get { return Rest.MsgId; }
  101. }
  102. #region Interface
  103. /// <summary>
  104. /// The plugin (RestHandler) calls this method to allocate the request
  105. /// state carrier for a new request. It is destroyed when the request
  106. /// completes. All request-instance specific state is kept here. This
  107. /// is registered when this service provider is registered.
  108. /// </summary>
  109. /// <param name=request>Inbound HTTP request information</param>
  110. /// <param name=response>Outbound HTTP request information</param>
  111. /// <param name=qPrefix>REST service domain prefix</param>
  112. /// <returns>A RequestData instance suitable for this service</returns>
  113. private RequestData Allocate(OSHttpRequest request, OSHttpResponse response, string prefix)
  114. {
  115. return (RequestData) new AppearanceRequestData(request, response, prefix);
  116. }
  117. /// <summary>
  118. /// This method is registered with the handler when this service provider
  119. /// is initialized. It is called whenever the plug-in identifies this service
  120. /// provider as the best match for a given request.
  121. /// It handles all aspects of inventory REST processing, i.e. /admin/inventory
  122. /// </summary>
  123. /// <param name=hdata>A consolidated HTTP request work area</param>
  124. private void DoAppearance(RequestData hdata)
  125. {
  126. AppearanceRequestData rdata = (AppearanceRequestData) hdata;
  127. Rest.Log.DebugFormat("{0} DoAppearance ENTRY", MsgId);
  128. // If we're disabled, do nothing.
  129. if (!enabled)
  130. {
  131. return;
  132. }
  133. // Now that we know this is a serious attempt to
  134. // access inventory data, we should find out who
  135. // is asking, and make sure they are authorized
  136. // to do so. We need to validate the caller's
  137. // identity before revealing anything about the
  138. // status quo. Authenticate throws an exception
  139. // via Fail if no identity information is present.
  140. //
  141. // With the present HTTP server we can't use the
  142. // builtin authentication mechanisms because they
  143. // would be enforced for all in-bound requests.
  144. // Instead we look at the headers ourselves and
  145. // handle authentication directly.
  146. try
  147. {
  148. if (!rdata.IsAuthenticated)
  149. {
  150. rdata.Fail(Rest.HttpStatusCodeNotAuthorized,String.Format("user \"{0}\" could not be authenticated", rdata.userName));
  151. }
  152. }
  153. catch (RestException e)
  154. {
  155. if (e.statusCode == Rest.HttpStatusCodeNotAuthorized)
  156. {
  157. Rest.Log.WarnFormat("{0} User not authenticated", MsgId);
  158. Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId, rdata.request.Headers.Get("Authorization"));
  159. }
  160. else
  161. {
  162. Rest.Log.ErrorFormat("{0} User authentication failed", MsgId);
  163. Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId, rdata.request.Headers.Get("Authorization"));
  164. }
  165. throw (e);
  166. }
  167. Rest.Log.DebugFormat("{0} Authenticated {1}", MsgId, rdata.userName);
  168. // We can only get here if we are authorized
  169. //
  170. // The requestor may have specified an UUID or
  171. // a conjoined FirstName LastName string. We'll
  172. // try both. If we fail with the first, UUID,
  173. // attempt, we try the other. As an example, the
  174. // URI for a valid inventory request might be:
  175. //
  176. // http://<host>:<port>/admin/inventory/Arthur Dent
  177. //
  178. // Indicating that this is an inventory request for
  179. // an avatar named Arthur Dent. This is ALL that is
  180. // required to designate a GET for an entire
  181. // inventory.
  182. //
  183. // Do we have at least a user agent name?
  184. if (rdata.Parameters.Length < 1)
  185. {
  186. Rest.Log.WarnFormat("{0} Appearance: No user agent identifier specified", MsgId);
  187. rdata.Fail(Rest.HttpStatusCodeBadRequest, "no user identity specified");
  188. }
  189. // The first parameter MUST be the agent identification, either an UUID
  190. // or a space-separated First-name Last-Name specification. We check for
  191. // an UUID first, if anyone names their character using a valid UUID
  192. // that identifies another existing avatar will cause this a problem...
  193. try
  194. {
  195. rdata.uuid = new UUID(rdata.Parameters[PARM_USERID]);
  196. Rest.Log.DebugFormat("{0} UUID supplied", MsgId);
  197. rdata.userProfile = Rest.UserServices.GetUserProfile(rdata.uuid);
  198. }
  199. catch
  200. {
  201. string[] names = rdata.Parameters[PARM_USERID].Split(Rest.CA_SPACE);
  202. if (names.Length == 2)
  203. {
  204. Rest.Log.DebugFormat("{0} Agent Name supplied [2]", MsgId);
  205. rdata.userProfile = Rest.UserServices.GetUserProfile(names[0],names[1]);
  206. }
  207. else
  208. {
  209. Rest.Log.WarnFormat("{0} A Valid UUID or both first and last names must be specified", MsgId);
  210. rdata.Fail(Rest.HttpStatusCodeBadRequest, "invalid user identity");
  211. }
  212. }
  213. // If the user profile is null then either the server is broken, or the
  214. // user is not known. We always assume the latter case.
  215. if (rdata.userProfile != null)
  216. {
  217. Rest.Log.DebugFormat("{0} User profile obtained for agent {1} {2}",
  218. MsgId, rdata.userProfile.FirstName, rdata.userProfile.SurName);
  219. }
  220. else
  221. {
  222. Rest.Log.WarnFormat("{0} No user profile for {1}", MsgId, rdata.path);
  223. rdata.Fail(Rest.HttpStatusCodeNotFound, "unrecognized user identity");
  224. }
  225. // If we get to here, then we have effectively validated the user's
  226. switch (rdata.method)
  227. {
  228. case Rest.HEAD : // Do the processing, set the status code, suppress entity
  229. DoGet(rdata);
  230. rdata.buffer = null;
  231. break;
  232. case Rest.GET : // Do the processing, set the status code, return entity
  233. DoGet(rdata);
  234. break;
  235. case Rest.PUT : // Update named element
  236. DoUpdate(rdata);
  237. break;
  238. case Rest.POST : // Add new information to identified context.
  239. DoExtend(rdata);
  240. break;
  241. case Rest.DELETE : // Delete information
  242. DoDelete(rdata);
  243. break;
  244. default :
  245. Rest.Log.WarnFormat("{0} Method {1} not supported for {2}",
  246. MsgId, rdata.method, rdata.path);
  247. rdata.Fail(Rest.HttpStatusCodeMethodNotAllowed,
  248. String.Format("{0} not supported", rdata.method));
  249. break;
  250. }
  251. }
  252. #endregion Interface
  253. #region method-specific processing
  254. /// <summary>
  255. /// This method implements GET processing for user's appearance.
  256. /// </summary>
  257. /// <param name=rdata>HTTP service request work area</param>
  258. private void DoGet(AppearanceRequestData rdata)
  259. {
  260. rdata.userAppearance = Rest.AvatarServices.GetUserAppearance(rdata.userProfile.ID);
  261. if (rdata.userAppearance == null)
  262. {
  263. rdata.Fail(Rest.HttpStatusCodeNoContent,
  264. String.Format("appearance data not found for user {0} {1}",
  265. rdata.userProfile.FirstName, rdata.userProfile.SurName));
  266. }
  267. rdata.initXmlWriter();
  268. FormatUserAppearance(rdata);
  269. // Indicate a successful request
  270. rdata.Complete();
  271. // Send the response to the user. The body will be implicitly
  272. // constructed from the result of the XML writer.
  273. rdata.Respond(String.Format("Appearance {0} Normal completion", rdata.method));
  274. }
  275. /// <summary>
  276. /// POST adds NEW information to the user profile database.
  277. /// This effectively resets the appearance before applying those
  278. /// characteristics supplied in the request.
  279. /// </summary>
  280. private void DoExtend(AppearanceRequestData rdata)
  281. {
  282. bool created = false;
  283. bool modified = false;
  284. string newnode = String.Empty;
  285. Rest.Log.DebugFormat("{0} POST ENTRY", MsgId);
  286. //AvatarAppearance old = Rest.AvatarServices.GetUserAppearance(rdata.userProfile.ID);
  287. rdata.userAppearance = new AvatarAppearance();
  288. // Although the following behavior is admitted by HTTP I am becoming
  289. // increasingly doubtful that it is appropriate for REST. If I attempt to
  290. // add a new record, and it already exists, then it seems to me that the
  291. // attempt should fail, rather than update the existing record.
  292. if (GetUserAppearance(rdata))
  293. {
  294. modified = rdata.userAppearance != null;
  295. created = !modified;
  296. Rest.AvatarServices.UpdateUserAppearance(rdata.userProfile.ID, rdata.userAppearance);
  297. // Rest.UserServices.UpdateUserProfile(rdata.userProfile);
  298. }
  299. else
  300. {
  301. created = true;
  302. Rest.AvatarServices.UpdateUserAppearance(rdata.userProfile.ID, rdata.userAppearance);
  303. // Rest.UserServices.UpdateUserProfile(rdata.userProfile);
  304. }
  305. if (created)
  306. {
  307. newnode = String.Format("{0} {1}", rdata.userProfile.FirstName,
  308. rdata.userProfile.SurName);
  309. // Must include a location header with a URI that identifies the new resource.
  310. rdata.AddHeader(Rest.HttpHeaderLocation,String.Format("http://{0}{1}:{2}{3}{4}",
  311. rdata.hostname,rdata.port,rdata.path,Rest.UrlPathSeparator, newnode));
  312. rdata.Complete(Rest.HttpStatusCodeCreated);
  313. }
  314. else
  315. {
  316. if (modified)
  317. {
  318. rdata.Complete(Rest.HttpStatusCodeOK);
  319. }
  320. else
  321. {
  322. rdata.Complete(Rest.HttpStatusCodeNoContent);
  323. }
  324. }
  325. rdata.Respond(String.Format("Appearance {0} : Normal completion", rdata.method));
  326. }
  327. /// <summary>
  328. /// This updates the user's appearance. not all aspects need to be provided,
  329. /// only those supplied will be changed.
  330. /// </summary>
  331. private void DoUpdate(AppearanceRequestData rdata)
  332. {
  333. bool created = false;
  334. bool modified = false;
  335. rdata.userAppearance = Rest.AvatarServices.GetUserAppearance(rdata.userProfile.ID);
  336. // If the user exists then this is considered a modification regardless
  337. // of what may, or may not be, specified in the payload.
  338. if (rdata.userAppearance != null)
  339. {
  340. modified = true;
  341. Rest.AvatarServices.UpdateUserAppearance(rdata.userProfile.ID, rdata.userAppearance);
  342. Rest.UserServices.UpdateUserProfile(rdata.userProfile);
  343. }
  344. if (created)
  345. {
  346. rdata.Complete(Rest.HttpStatusCodeCreated);
  347. }
  348. else
  349. {
  350. if (modified)
  351. {
  352. rdata.Complete(Rest.HttpStatusCodeOK);
  353. }
  354. else
  355. {
  356. rdata.Complete(Rest.HttpStatusCodeNoContent);
  357. }
  358. }
  359. rdata.Respond(String.Format("Appearance {0} : Normal completion", rdata.method));
  360. }
  361. /// <summary>
  362. /// Delete the specified user's appearance. This actually performs a reset
  363. /// to the default avatar appearance, if the info is already there.
  364. /// Existing ownership is preserved. All prior updates are lost and can not
  365. /// be recovered.
  366. /// </summary>
  367. private void DoDelete(AppearanceRequestData rdata)
  368. {
  369. AvatarAppearance old = Rest.AvatarServices.GetUserAppearance(rdata.userProfile.ID);
  370. if (old != null)
  371. {
  372. rdata.userAppearance = new AvatarAppearance();
  373. rdata.userAppearance.Owner = old.Owner;
  374. Rest.AvatarServices.UpdateUserAppearance(rdata.userProfile.ID, rdata.userAppearance);
  375. rdata.Complete();
  376. }
  377. else
  378. {
  379. rdata.Complete(Rest.HttpStatusCodeNoContent);
  380. }
  381. rdata.Respond(String.Format("Appearance {0} : Normal completion", rdata.method));
  382. }
  383. #endregion method-specific processing
  384. private bool GetUserAppearance(AppearanceRequestData rdata)
  385. {
  386. XmlReader xml;
  387. bool indata = false;
  388. rdata.initXmlReader();
  389. xml = rdata.reader;
  390. while (xml.Read())
  391. {
  392. switch (xml.NodeType)
  393. {
  394. case XmlNodeType.Element :
  395. switch (xml.Name)
  396. {
  397. case "Appearance" :
  398. if (xml.MoveToAttribute("Height"))
  399. {
  400. rdata.userAppearance.AvatarHeight = (float) Convert.ToDouble(xml.Value);
  401. indata = true;
  402. }
  403. if (xml.MoveToAttribute("Owner"))
  404. {
  405. rdata.userAppearance.Owner = (UUID)xml.Value;
  406. indata = true;
  407. }
  408. if (xml.MoveToAttribute("Serial"))
  409. {
  410. rdata.userAppearance.Serial = Convert.ToInt32(xml.Value);
  411. indata = true;
  412. }
  413. break;
  414. case "Body" :
  415. if (xml.MoveToAttribute("Item"))
  416. {
  417. rdata.userAppearance.BodyItem = (UUID)xml.Value;
  418. indata = true;
  419. }
  420. if (xml.MoveToAttribute("Asset"))
  421. {
  422. rdata.userAppearance.BodyAsset = (UUID)xml.Value;
  423. indata = true;
  424. }
  425. break;
  426. case "Skin" :
  427. if (xml.MoveToAttribute("Item"))
  428. {
  429. rdata.userAppearance.SkinItem = (UUID)xml.Value;
  430. indata = true;
  431. }
  432. if (xml.MoveToAttribute("Asset"))
  433. {
  434. rdata.userAppearance.SkinAsset = (UUID)xml.Value;
  435. indata = true;
  436. }
  437. break;
  438. case "Hair" :
  439. if (xml.MoveToAttribute("Item"))
  440. {
  441. rdata.userAppearance.HairItem = (UUID)xml.Value;
  442. indata = true;
  443. }
  444. if (xml.MoveToAttribute("Asset"))
  445. {
  446. rdata.userAppearance.HairAsset = (UUID)xml.Value;
  447. indata = true;
  448. }
  449. break;
  450. case "Eyes" :
  451. if (xml.MoveToAttribute("Item"))
  452. {
  453. rdata.userAppearance.EyesItem = (UUID)xml.Value;
  454. indata = true;
  455. }
  456. if (xml.MoveToAttribute("Asset"))
  457. {
  458. rdata.userAppearance.EyesAsset = (UUID)xml.Value;
  459. indata = true;
  460. }
  461. break;
  462. case "Shirt" :
  463. if (xml.MoveToAttribute("Item"))
  464. {
  465. rdata.userAppearance.ShirtItem = (UUID)xml.Value;
  466. indata = true;
  467. }
  468. if (xml.MoveToAttribute("Asset"))
  469. {
  470. rdata.userAppearance.ShirtAsset = (UUID)xml.Value;
  471. indata = true;
  472. }
  473. break;
  474. case "Pants" :
  475. if (xml.MoveToAttribute("Item"))
  476. {
  477. rdata.userAppearance.PantsItem = (UUID)xml.Value;
  478. indata = true;
  479. }
  480. if (xml.MoveToAttribute("Asset"))
  481. {
  482. rdata.userAppearance.PantsAsset = (UUID)xml.Value;
  483. indata = true;
  484. }
  485. break;
  486. case "Shoes" :
  487. if (xml.MoveToAttribute("Item"))
  488. {
  489. rdata.userAppearance.ShoesItem = (UUID)xml.Value;
  490. indata = true;
  491. }
  492. if (xml.MoveToAttribute("Asset"))
  493. {
  494. rdata.userAppearance.ShoesAsset = (UUID)xml.Value;
  495. indata = true;
  496. }
  497. break;
  498. case "Socks" :
  499. if (xml.MoveToAttribute("Item"))
  500. {
  501. rdata.userAppearance.SocksItem = (UUID)xml.Value;
  502. indata = true;
  503. }
  504. if (xml.MoveToAttribute("Asset"))
  505. {
  506. rdata.userAppearance.SocksAsset = (UUID)xml.Value;
  507. indata = true;
  508. }
  509. break;
  510. case "Jacket" :
  511. if (xml.MoveToAttribute("Item"))
  512. {
  513. rdata.userAppearance.JacketItem = (UUID)xml.Value;
  514. indata = true;
  515. }
  516. if (xml.MoveToAttribute("Asset"))
  517. {
  518. rdata.userAppearance.JacketAsset = (UUID)xml.Value;
  519. indata = true;
  520. }
  521. break;
  522. case "Gloves" :
  523. if (xml.MoveToAttribute("Item"))
  524. {
  525. rdata.userAppearance.GlovesItem = (UUID)xml.Value;
  526. indata = true;
  527. }
  528. if (xml.MoveToAttribute("Asset"))
  529. {
  530. rdata.userAppearance.GlovesAsset = (UUID)xml.Value;
  531. indata = true;
  532. }
  533. break;
  534. case "UnderShirt" :
  535. if (xml.MoveToAttribute("Item"))
  536. {
  537. rdata.userAppearance.UnderShirtItem = (UUID)xml.Value;
  538. indata = true;
  539. }
  540. if (xml.MoveToAttribute("Asset"))
  541. {
  542. rdata.userAppearance.UnderShirtAsset = (UUID)xml.Value;
  543. indata = true;
  544. }
  545. break;
  546. case "UnderPants" :
  547. if (xml.MoveToAttribute("Item"))
  548. {
  549. rdata.userAppearance.UnderPantsItem = (UUID)xml.Value;
  550. indata = true;
  551. }
  552. if (xml.MoveToAttribute("Asset"))
  553. {
  554. rdata.userAppearance.UnderPantsAsset = (UUID)xml.Value;
  555. indata = true;
  556. }
  557. break;
  558. case "Skirt" :
  559. if (xml.MoveToAttribute("Item"))
  560. {
  561. rdata.userAppearance.SkirtItem = (UUID)xml.Value;
  562. indata = true;
  563. }
  564. if (xml.MoveToAttribute("Asset"))
  565. {
  566. rdata.userAppearance.SkirtAsset = (UUID)xml.Value;
  567. indata = true;
  568. }
  569. break;
  570. case "Attachment" :
  571. {
  572. int ap;
  573. UUID asset;
  574. UUID item;
  575. if (xml.MoveToAttribute("AtPoint"))
  576. {
  577. ap = Convert.ToInt32(xml.Value);
  578. if (xml.MoveToAttribute("Asset"))
  579. {
  580. asset = new UUID(xml.Value);
  581. if (xml.MoveToAttribute("Asset"))
  582. {
  583. item = new UUID(xml.Value);
  584. rdata.userAppearance.SetAttachment(ap, item, asset);
  585. indata = true;
  586. }
  587. }
  588. }
  589. }
  590. break;
  591. case "Texture" :
  592. if (xml.MoveToAttribute("Default"))
  593. {
  594. rdata.userAppearance.Texture = new Primitive.TextureEntry(new UUID(xml.Value));
  595. indata = true;
  596. }
  597. break;
  598. case "Face" :
  599. {
  600. uint index;
  601. if (xml.MoveToAttribute("Index"))
  602. {
  603. index = Convert.ToUInt32(xml.Value);
  604. if (xml.MoveToAttribute("Id"))
  605. {
  606. rdata.userAppearance.Texture.CreateFace(index).TextureID = new UUID(xml.Value);
  607. indata = true;
  608. }
  609. }
  610. }
  611. break;
  612. case "VisualParameters" :
  613. {
  614. xml.ReadContentAsBase64(rdata.userAppearance.VisualParams,
  615. 0, rdata.userAppearance.VisualParams.Length);
  616. indata = true;
  617. }
  618. break;
  619. }
  620. break;
  621. }
  622. }
  623. return indata;
  624. }
  625. private void FormatPart(AppearanceRequestData rdata, string part, UUID item, UUID asset)
  626. {
  627. if (item != UUID.Zero || asset != UUID.Zero)
  628. {
  629. rdata.writer.WriteStartElement(part);
  630. if (item != UUID.Zero)
  631. {
  632. rdata.writer.WriteAttributeString("Item",item.ToString());
  633. }
  634. if (asset != UUID.Zero)
  635. {
  636. rdata.writer.WriteAttributeString("Asset",asset.ToString());
  637. }
  638. rdata.writer.WriteEndElement();
  639. }
  640. }
  641. private void FormatUserAppearance(AppearanceRequestData rdata)
  642. {
  643. Rest.Log.DebugFormat("{0} FormatUserAppearance", MsgId);
  644. if (rdata.userAppearance != null)
  645. {
  646. Rest.Log.DebugFormat("{0} FormatUserAppearance: appearance object exists", MsgId);
  647. rdata.writer.WriteStartElement("Appearance");
  648. rdata.writer.WriteAttributeString("Height", rdata.userAppearance.AvatarHeight.ToString());
  649. if (rdata.userAppearance.Owner != UUID.Zero)
  650. rdata.writer.WriteAttributeString("Owner", rdata.userAppearance.Owner.ToString());
  651. rdata.writer.WriteAttributeString("Serial", rdata.userAppearance.Serial.ToString());
  652. FormatPart(rdata, "Body", rdata.userAppearance.BodyItem, rdata.userAppearance.BodyAsset);
  653. FormatPart(rdata, "Skin", rdata.userAppearance.SkinItem, rdata.userAppearance.SkinAsset);
  654. FormatPart(rdata, "Hair", rdata.userAppearance.HairItem, rdata.userAppearance.HairAsset);
  655. FormatPart(rdata, "Eyes", rdata.userAppearance.EyesItem, rdata.userAppearance.EyesAsset);
  656. FormatPart(rdata, "Shirt", rdata.userAppearance.ShirtItem, rdata.userAppearance.ShirtAsset);
  657. FormatPart(rdata, "Pants", rdata.userAppearance.PantsItem, rdata.userAppearance.PantsAsset);
  658. FormatPart(rdata, "Skirt", rdata.userAppearance.SkirtItem, rdata.userAppearance.SkirtAsset);
  659. FormatPart(rdata, "Shoes", rdata.userAppearance.ShoesItem, rdata.userAppearance.ShoesAsset);
  660. FormatPart(rdata, "Socks", rdata.userAppearance.SocksItem, rdata.userAppearance.SocksAsset);
  661. FormatPart(rdata, "Jacket", rdata.userAppearance.JacketItem, rdata.userAppearance.JacketAsset);
  662. FormatPart(rdata, "Gloves", rdata.userAppearance.GlovesItem, rdata.userAppearance.GlovesAsset);
  663. FormatPart(rdata, "UnderShirt", rdata.userAppearance.UnderShirtItem, rdata.userAppearance.UnderShirtAsset);
  664. FormatPart(rdata, "UnderPants", rdata.userAppearance.UnderPantsItem, rdata.userAppearance.UnderPantsAsset);
  665. Hashtable attachments = rdata.userAppearance.GetAttachments();
  666. if (attachments != null)
  667. {
  668. Rest.Log.DebugFormat("{0} FormatUserAppearance: Formatting attachments", MsgId);
  669. rdata.writer.WriteStartElement("Attachments");
  670. for (int i = 0; i < attachments.Count; i++)
  671. {
  672. Hashtable attachment = attachments[i] as Hashtable;
  673. rdata.writer.WriteStartElement("Attachment");
  674. rdata.writer.WriteAttributeString("AtPoint", i.ToString());
  675. rdata.writer.WriteAttributeString("Item", (string) attachment["item"]);
  676. rdata.writer.WriteAttributeString("Asset", (string) attachment["asset"]);
  677. rdata.writer.WriteEndElement();
  678. }
  679. rdata.writer.WriteEndElement();
  680. }
  681. Primitive.TextureEntry texture = rdata.userAppearance.Texture;
  682. if (texture != null && (texture.DefaultTexture != null || texture.FaceTextures != null))
  683. {
  684. Rest.Log.DebugFormat("{0} FormatUserAppearance: Formatting textures", MsgId);
  685. rdata.writer.WriteStartElement("Texture");
  686. if (texture.DefaultTexture != null)
  687. {
  688. Rest.Log.DebugFormat("{0} FormatUserAppearance: Formatting default texture", MsgId);
  689. rdata.writer.WriteAttributeString("Default",
  690. texture.DefaultTexture.TextureID.ToString());
  691. }
  692. if (texture.FaceTextures != null)
  693. {
  694. Rest.Log.DebugFormat("{0} FormatUserAppearance: Formatting face textures", MsgId);
  695. for (int i=0; i<texture.FaceTextures.Length;i++)
  696. {
  697. if (texture.FaceTextures[i] != null)
  698. {
  699. rdata.writer.WriteStartElement("Face");
  700. rdata.writer.WriteAttributeString("Index", i.ToString());
  701. rdata.writer.WriteAttributeString("Id",
  702. texture.FaceTextures[i].TextureID.ToString());
  703. rdata.writer.WriteEndElement();
  704. }
  705. }
  706. }
  707. rdata.writer.WriteEndElement();
  708. }
  709. Rest.Log.DebugFormat("{0} FormatUserAppearance: Formatting visual parameters", MsgId);
  710. rdata.writer.WriteStartElement("VisualParameters");
  711. rdata.writer.WriteBase64(rdata.userAppearance.VisualParams,0,
  712. rdata.userAppearance.VisualParams.Length);
  713. rdata.writer.WriteEndElement();
  714. rdata.writer.WriteFullEndElement();
  715. }
  716. Rest.Log.DebugFormat("{0} FormatUserAppearance: completed", MsgId);
  717. return;
  718. }
  719. #region appearance RequestData extension
  720. internal class AppearanceRequestData : RequestData
  721. {
  722. /// <summary>
  723. /// These are the inventory specific request/response state
  724. /// extensions.
  725. /// </summary>
  726. internal UUID uuid = UUID.Zero;
  727. internal UserProfileData userProfile = null;
  728. internal AvatarAppearance userAppearance = null;
  729. internal AppearanceRequestData(OSHttpRequest request, OSHttpResponse response, string prefix)
  730. : base(request, response, prefix)
  731. {
  732. }
  733. }
  734. #endregion Appearance RequestData extension
  735. }
  736. }