RestAppearanceServices.cs 36 KB

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