diff options
Diffstat (limited to '')
-rw-r--r-- | OpenSim/ApplicationPlugins/Rest/Inventory/RestAppearanceServices.cs | 826 | ||||
-rw-r--r-- | OpenSim/ApplicationPlugins/Rest/rest.xsd | 276 |
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 | |||
28 | using System; | ||
29 | using System.Collections; | ||
30 | using System.Collections.Generic; | ||
31 | using System.IO; | ||
32 | using System.Threading; | ||
33 | using System.Xml; | ||
34 | using System.Drawing; | ||
35 | using OpenSim.Framework; | ||
36 | using OpenSim.Framework.Servers; | ||
37 | using OpenSim.Framework.Communications; | ||
38 | using OpenSim.Framework.Communications.Cache; | ||
39 | using OpenMetaverse; | ||
40 | using OpenMetaverse.Imaging; | ||
41 | using Nini.Config; | ||
42 | |||
43 | namespace 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> | ||