diff options
Diffstat (limited to 'OpenSim/ApplicationPlugins/Rest/Inventory/RestInventoryServices.cs')
-rw-r--r-- | OpenSim/ApplicationPlugins/Rest/Inventory/RestInventoryServices.cs | 295 |
1 files changed, 199 insertions, 96 deletions
diff --git a/OpenSim/ApplicationPlugins/Rest/Inventory/RestInventoryServices.cs b/OpenSim/ApplicationPlugins/Rest/Inventory/RestInventoryServices.cs index 65603c5..5de2cb4 100644 --- a/OpenSim/ApplicationPlugins/Rest/Inventory/RestInventoryServices.cs +++ b/OpenSim/ApplicationPlugins/Rest/Inventory/RestInventoryServices.cs | |||
@@ -31,6 +31,7 @@ using System.Collections.Generic; | |||
31 | using System.IO; | 31 | using System.IO; |
32 | using System.Threading; | 32 | using System.Threading; |
33 | using System.Xml; | 33 | using System.Xml; |
34 | using OpenJPEGNet; | ||
34 | using OpenSim.Framework; | 35 | using OpenSim.Framework; |
35 | using OpenSim.Framework.Servers; | 36 | using OpenSim.Framework.Servers; |
36 | using OpenSim.Framework.Communications; | 37 | using OpenSim.Framework.Communications; |
@@ -44,35 +45,33 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
44 | public class RestInventoryServices : IRest | 45 | public class RestInventoryServices : IRest |
45 | { | 46 | { |
46 | 47 | ||
47 | private string key = "inventory"; | ||
48 | private bool enabled = false; | 48 | private bool enabled = false; |
49 | private string qPrefix = "inventory"; | 49 | private string qPrefix = "inventory"; |
50 | 50 | ||
51 | // A simple constructor is used to handle any once-only | 51 | /// <summary> |
52 | // initialization of working classes. | 52 | /// A simple constructor is used to handle any once-only |
53 | /// initialization of working classes. | ||
54 | /// </summary> | ||
53 | 55 | ||
54 | public RestInventoryServices(RestHandler p_rest) | 56 | public RestInventoryServices() |
55 | { | 57 | { |
56 | 58 | ||
57 | Rest.Log.InfoFormat("{0} Inventory services initializing", MsgId); | 59 | Rest.Log.InfoFormat("{0} Inventory services initializing", MsgId); |
58 | Rest.Log.InfoFormat("{0} Using REST Implementation Version {1}", MsgId, Rest.Version); | 60 | Rest.Log.InfoFormat("{0} Using REST Implementation Version {1}", MsgId, Rest.Version); |
59 | 61 | ||
60 | // Update to reflect the full prefix if not absolute | 62 | // If a relative path was specified for the handler's domain, |
63 | // add the standard prefix to make it absolute, e.g. /admin | ||
61 | 64 | ||
62 | if (!qPrefix.StartsWith(Rest.UrlPathSeparator)) | 65 | if (!qPrefix.StartsWith(Rest.UrlPathSeparator)) |
63 | { | 66 | { |
64 | qPrefix = Rest.Prefix + Rest.UrlPathSeparator + qPrefix; | 67 | qPrefix = Rest.Prefix + Rest.UrlPathSeparator + qPrefix; |
65 | } | 68 | } |
66 | 69 | ||
67 | // Authentication domain | 70 | // Register interface using the absolute URI. |
68 | |||
69 | Rest.Domains.Add(key, Rest.Config.GetString("inventory-domain",qPrefix)); | ||
70 | |||
71 | // Register interface | ||
72 | 71 | ||
73 | Rest.Plugin.AddPathHandler(DoInventory,qPrefix,Allocate); | 72 | Rest.Plugin.AddPathHandler(DoInventory,qPrefix,Allocate); |
74 | 73 | ||
75 | // Activate | 74 | // Activate if everything went OK |
76 | 75 | ||
77 | enabled = true; | 76 | enabled = true; |
78 | 77 | ||
@@ -80,16 +79,20 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
80 | 79 | ||
81 | } | 80 | } |
82 | 81 | ||
83 | // Post-construction, pre-enabled initialization opportunity | 82 | /// <summary> |
84 | // Not currently exploited. | 83 | /// Post-construction, pre-enabled initialization opportunity |
84 | /// Not currently exploited. | ||
85 | /// </summary> | ||
85 | 86 | ||
86 | public void Initialize() | 87 | public void Initialize() |
87 | { | 88 | { |
88 | } | 89 | } |
89 | 90 | ||
90 | // Called by the plug-in to halt REST processing. Local processing is | 91 | /// <summary> |
91 | // disabled, and control blocks until all current processing has | 92 | /// Called by the plug-in to halt REST processing. Local processing is |
92 | // completed. No new processing will be started | 93 | /// disabled, and control blocks until all current processing has |
94 | /// completed. No new processing will be started | ||
95 | /// </summary> | ||
93 | 96 | ||
94 | public void Close() | 97 | public void Close() |
95 | { | 98 | { |
@@ -97,7 +100,10 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
97 | Rest.Log.InfoFormat("{0} Inventory services closing down", MsgId); | 100 | Rest.Log.InfoFormat("{0} Inventory services closing down", MsgId); |
98 | } | 101 | } |
99 | 102 | ||
100 | // Convenient properties | 103 | /// <summary> |
104 | /// This property is declared locally because it is used a lot and | ||
105 | /// brevity is nice. | ||
106 | /// </summary> | ||
101 | 107 | ||
102 | internal string MsgId | 108 | internal string MsgId |
103 | { | 109 | { |
@@ -106,15 +112,22 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
106 | 112 | ||
107 | #region Interface | 113 | #region Interface |
108 | 114 | ||
115 | /// <summary> | ||
116 | /// The plugin (RestHandler) calls this method to allocate the request | ||
117 | /// state carrier for a new request. It is destroyed when the request | ||
118 | /// completes. All request-instance specific state is kept here. This | ||
119 | /// is registered when this service provider is registered. | ||
120 | /// </summary> | ||
121 | |||
109 | private RequestData Allocate(OSHttpRequest request, OSHttpResponse response) | 122 | private RequestData Allocate(OSHttpRequest request, OSHttpResponse response) |
110 | { | 123 | { |
111 | return (RequestData) new InventoryRequestData(request, response, qPrefix); | 124 | return (RequestData) new InventoryRequestData(request, response, qPrefix); |
112 | } | 125 | } |
113 | 126 | ||
114 | /// <summary> | 127 | /// <summary> |
115 | /// This method is registered with the handler when this class is | 128 | /// This method is registered with the handler when this service provider |
116 | /// initialized. It is called whenever the URI includes this handler's | 129 | /// is initialized. It is called whenever the plug-in identifies this service |
117 | /// prefix string. | 130 | /// provider as the best match. |
118 | /// It handles all aspects of inventory REST processing. | 131 | /// It handles all aspects of inventory REST processing. |
119 | /// </summary> | 132 | /// </summary> |
120 | 133 | ||
@@ -125,7 +138,8 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
125 | 138 | ||
126 | Rest.Log.DebugFormat("{0} DoInventory ENTRY", MsgId); | 139 | Rest.Log.DebugFormat("{0} DoInventory ENTRY", MsgId); |
127 | 140 | ||
128 | // We're disabled | 141 | // If we're disabled, do nothing. |
142 | |||
129 | if (!enabled) | 143 | if (!enabled) |
130 | { | 144 | { |
131 | return; | 145 | return; |
@@ -169,23 +183,32 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
169 | 183 | ||
170 | Rest.Log.DebugFormat("{0} Authenticated {1}", MsgId, rdata.userName); | 184 | Rest.Log.DebugFormat("{0} Authenticated {1}", MsgId, rdata.userName); |
171 | 185 | ||
172 | // We can only get here if we're authorized | 186 | /// <remarks> |
173 | // | 187 | /// We can only get here if we are authorized |
174 | // The requestor may have specified an LLUUID or | 188 | /// |
175 | // a conjoined FirstNameLastName string. We'll | 189 | /// The requestor may have specified an LLUUID or |
176 | // try both. If we fail with the first, UUID, | 190 | /// a conjoined FirstName LastName string. We'll |
177 | // attempt, then we need two nodes to construct | 191 | /// try both. If we fail with the first, UUID, |
178 | // a valid avatar name. | 192 | /// attempt, we try the other. As an example, the |
193 | /// URI for a valid inventory request might be: | ||
194 | /// | ||
195 | /// http://<host>:<port>/admin/inventory/Arthur Dent | ||
196 | /// | ||
197 | /// Indicating that this is an inventory request for | ||
198 | /// an avatar named Arthur Dent. This is ALl that is | ||
199 | /// required to designate a GET for an entire | ||
200 | /// inventory. | ||
201 | /// </remarks> | ||
179 | 202 | ||
180 | // Do we have at least a user agent name? | 203 | // Do we have at least a user agent name? |
181 | 204 | ||
182 | if (rdata.parameters.Length < 1) | 205 | if (rdata.parameters.Length < 1) |
183 | { | 206 | { |
184 | Rest.Log.WarnFormat("{0} Inventory: No user agent identifier specified", MsgId); | 207 | Rest.Log.WarnFormat("{0} Inventory: No user agent identifier specified", MsgId); |
185 | rdata.Fail(Rest.HttpStatusCodeBadRequest, Rest.HttpStatusDescBadRequest); | 208 | rdata.Fail(Rest.HttpStatusCodeBadRequest, Rest.HttpStatusDescBadRequest+": No user identity specified"); |
186 | } | 209 | } |
187 | 210 | ||
188 | // The next parameter MUST be the agent identification, either an LLUUID | 211 | // The first parameter MUST be the agent identification, either an LLUUID |
189 | // or a space-separated First-name Last-Name specification. | 212 | // or a space-separated First-name Last-Name specification. |
190 | 213 | ||
191 | try | 214 | try |
@@ -205,10 +228,13 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
205 | else | 228 | else |
206 | { | 229 | { |
207 | Rest.Log.DebugFormat("{0} A Valid UUID or both first and last names must be specified", MsgId); | 230 | Rest.Log.DebugFormat("{0} A Valid UUID or both first and last names must be specified", MsgId); |
208 | rdata.Fail(Rest.HttpStatusCodeBadRequest, Rest.HttpStatusDescBadRequest); | 231 | rdata.Fail(Rest.HttpStatusCodeBadRequest, Rest.HttpStatusDescBadRequest+": invalid user identity"); |
209 | } | 232 | } |
210 | } | 233 | } |
211 | 234 | ||
235 | // If the user rpofile is null then either the server is broken, or the | ||
236 | // user is not known. We always assume the latter case. | ||
237 | |||
212 | if (rdata.userProfile != null) | 238 | if (rdata.userProfile != null) |
213 | { | 239 | { |
214 | Rest.Log.DebugFormat("{0} Profile obtained for agent {1} {2}", | 240 | Rest.Log.DebugFormat("{0} Profile obtained for agent {1} {2}", |
@@ -217,11 +243,20 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
217 | else | 243 | else |
218 | { | 244 | { |
219 | Rest.Log.DebugFormat("{0} No profile for {1}", MsgId, rdata.path); | 245 | Rest.Log.DebugFormat("{0} No profile for {1}", MsgId, rdata.path); |
220 | rdata.Fail(Rest.HttpStatusCodeNotFound,Rest.HttpStatusDescNotFound); | 246 | rdata.Fail(Rest.HttpStatusCodeNotFound,Rest.HttpStatusDescNotFound+": unrecognized user identity"); |
221 | } | 247 | } |
222 | 248 | ||
223 | // If we get to here, then we have successfully obtained an inventory | 249 | // If we get to here, then we have effectively validated the user's |
224 | // for the specified user. | 250 | // identity. Now we need to get the inventory. If the server does not |
251 | // have the inventory, we reject the request with an appropriate explanation. | ||
252 | // | ||
253 | // Note that inventory retrieval is an asynchronous event, we use the rdata | ||
254 | // class instance as the basis for our synchronization. | ||
255 | // | ||
256 | // TODO | ||
257 | // If something went wrong in inventory processing the thread could stall here | ||
258 | // indefinitely. There should be a watchdog timer to fail the request if the | ||
259 | // response is not recieved in a timely fashion. | ||
225 | 260 | ||
226 | rdata.uuid = rdata.userProfile.ID; | 261 | rdata.uuid = rdata.userProfile.ID; |
227 | 262 | ||
@@ -250,7 +285,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
250 | { | 285 | { |
251 | Rest.Log.DebugFormat("{0} Inventory is not available [1] for agent {1} {2}", | 286 | Rest.Log.DebugFormat("{0} Inventory is not available [1] for agent {1} {2}", |
252 | MsgId, rdata.userProfile.FirstName, rdata.userProfile.SurName); | 287 | MsgId, rdata.userProfile.FirstName, rdata.userProfile.SurName); |
253 | rdata.Fail(Rest.HttpStatusCodeServerError,Rest.HttpStatusDescServerError); | 288 | rdata.Fail(Rest.HttpStatusCodeServerError,Rest.HttpStatusDescServerError+": inventory retrieval failed"); |
254 | } | 289 | } |
255 | 290 | ||
256 | } | 291 | } |
@@ -258,7 +293,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
258 | { | 293 | { |
259 | Rest.Log.DebugFormat("{0} Inventory is not available for agent [3] {1} {2}", | 294 | Rest.Log.DebugFormat("{0} Inventory is not available for agent [3] {1} {2}", |
260 | MsgId, rdata.userProfile.FirstName, rdata.userProfile.SurName); | 295 | MsgId, rdata.userProfile.FirstName, rdata.userProfile.SurName); |
261 | rdata.Fail(Rest.HttpStatusCodeNotFound,Rest.HttpStatusDescNotFound); | 296 | rdata.Fail(Rest.HttpStatusCodeNotFound,Rest.HttpStatusDescNotFound+": no inventory for user"); |
262 | } | 297 | } |
263 | 298 | ||
264 | // If we get here, then we have successfully retrieved the user's information | 299 | // If we get here, then we have successfully retrieved the user's information |
@@ -292,7 +327,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
292 | Rest.Log.DebugFormat("{0} Method {1} not supported for {2}", | 327 | Rest.Log.DebugFormat("{0} Method {1} not supported for {2}", |
293 | MsgId, rdata.method, rdata.path); | 328 | MsgId, rdata.method, rdata.path); |
294 | rdata.Fail(Rest.HttpStatusCodeMethodNotAllowed, | 329 | rdata.Fail(Rest.HttpStatusCodeMethodNotAllowed, |
295 | Rest.HttpStatusDescMethodNotAllowed); | 330 | Rest.HttpStatusDescMethodNotAllowed+": "+rdata.method+" not supported"); |
296 | break; | 331 | break; |
297 | } | 332 | } |
298 | 333 | ||
@@ -315,10 +350,19 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
315 | 350 | ||
316 | rdata.writer.WriteStartElement(String.Empty,"Inventory",String.Empty); | 351 | rdata.writer.WriteStartElement(String.Empty,"Inventory",String.Empty); |
317 | 352 | ||
353 | // If there was only one parameter, then the entire | ||
354 | // inventory is being requested. | ||
355 | |||
318 | if (rdata.parameters.Length == 1) | 356 | if (rdata.parameters.Length == 1) |
319 | { | 357 | { |
320 | formatInventory(rdata, rdata.root, String.Empty); | 358 | formatInventory(rdata, rdata.root, String.Empty); |
321 | } | 359 | } |
360 | |||
361 | // If there are additional parameters, then these represent | ||
362 | // a path relative to the root of the inventory. This path | ||
363 | // must be traversed before we format the sub-tree thus | ||
364 | // identified. | ||
365 | |||
322 | else | 366 | else |
323 | { | 367 | { |
324 | traverseInventory(rdata, rdata.root, 1); | 368 | traverseInventory(rdata, rdata.root, 1); |
@@ -332,33 +376,35 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
332 | } | 376 | } |
333 | 377 | ||
334 | /// <summary> | 378 | /// <summary> |
335 | /// In the case of the inventory, and probably much else | 379 | /// In the case of the inventory, and probably in general, |
336 | /// the distinction between PUT and POST is not always | 380 | /// the distinction between PUT and POST is not always |
337 | /// easy to discern. Adding a directory can be viewed as | 381 | /// easy to discern. Adding a directory can be viewed as |
338 | /// an addition, or as a modification to the inventory as | 382 | /// an addition, or as a modification to the inventory as |
339 | /// a whole. | 383 | /// a whole. This is exacerbated by a lack of consistency |
384 | /// across different implementations. | ||
385 | /// | ||
386 | /// For OpenSim POST is an update and PUT is an addition. | ||
340 | /// | 387 | /// |
341 | /// The best distinction may be the relationship between | 388 | /// The best way to exaplain the distinction is to |
342 | /// the entity and the URI. If we view POST as an update, | 389 | /// consider the relationship between the URI and the |
343 | /// then the enity represents a replacement for the | 390 | /// entity in question. For POST, the URI identifies the |
344 | /// element named by the URI. If the operation is PUT, | 391 | /// entity to be modified or replaced. |
345 | /// then the URI describes the context into which the | 392 | /// If the operation is PUT,then the URI describes the |
346 | /// entity will be added. | 393 | /// context into which the new entity will be added. |
347 | /// | 394 | /// |
348 | /// As an example, suppose the URI contains: | 395 | /// As an example, suppose the URI contains: |
349 | /// /admin/inventory/Clothing | 396 | /// /admin/inventory/Clothing |
350 | /// Suppose the entity represents a Folder, called | ||
351 | /// "Clothes". | ||
352 | /// | ||
353 | /// A POST request will result in the replacement of | ||
354 | /// "Clothing" by "Clothes". Whereas a PUT request | ||
355 | /// would add Clothes as a sub-directory of Clothing. | ||
356 | /// | 397 | /// |
357 | /// This is the model followed by this implementation. | 398 | /// A POST request will result in some modification of |
399 | /// the folder or item named "Clothing". Whereas a PUT | ||
400 | /// request will add some new information into the | ||
401 | /// content identified by Clothing. It follows from this | ||
402 | /// that for PUT, the element identified by the URI must | ||
403 | /// be a folder. | ||
358 | /// </summary> | 404 | /// </summary> |
359 | 405 | ||
360 | /// <summary> | 406 | /// <summary> |
361 | /// PUT adds new information to the inventory at the | 407 | /// PUT adds new information to the inventory in the |
362 | /// context identified by the URI. | 408 | /// context identified by the URI. |
363 | /// </summary> | 409 | /// </summary> |
364 | 410 | ||
@@ -376,7 +422,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
376 | // exception. | 422 | // exception. |
377 | 423 | ||
378 | // It follows that we can only add information if the URI | 424 | // It follows that we can only add information if the URI |
379 | // has identified a folder. So only folder is supported | 425 | // has identified a folder. So only a type of folder is supported |
380 | // in this case. | 426 | // in this case. |
381 | 427 | ||
382 | if (typeof(InventoryFolderBase) == InventoryNode.GetType() || | 428 | if (typeof(InventoryFolderBase) == InventoryNode.GetType() || |
@@ -390,14 +436,19 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
390 | Rest.Log.DebugFormat("{0} {1}: Resource(s) will be added to folder {2}", | 436 | Rest.Log.DebugFormat("{0} {1}: Resource(s) will be added to folder {2}", |
391 | MsgId, rdata.method, rdata.path); | 437 | MsgId, rdata.method, rdata.path); |
392 | 438 | ||
393 | // Reconstitute inventory sub-tree from the XML supplied in the entity. | 439 | // Reconstitute the inventory sub-tree from the XML supplied in the entity. |
394 | // This is a stand-alone inventory subtree, not yet integrated into the | 440 | // The result is a stand-alone inventory subtree, not yet integrated into the |
395 | // existing tree. | 441 | // existing tree. An inventory collection consists of three components: |
442 | // [1] A (possibly empty) set of folders. | ||
443 | // [2] A (possibly empty) set of items. | ||
444 | // [3] A (possibly empty) set of assets. | ||
445 | // If all of these are empty, then the PUT is a harmless no-operation. | ||
396 | 446 | ||
397 | XmlInventoryCollection entity = ReconstituteEntity(rdata); | 447 | XmlInventoryCollection entity = ReconstituteEntity(rdata); |
398 | 448 | ||
399 | // Inlined assest included in entity. If anything fails, | 449 | // Inlined assets can be included in entity. These must be incorporated into |
400 | // return failure to requestor. | 450 | // the asset database before we attempt to update the inventory. If anything |
451 | // fails, return a failure to requestor. | ||
401 | 452 | ||
402 | if (entity.Assets.Count > 0) | 453 | if (entity.Assets.Count > 0) |
403 | { | 454 | { |
@@ -410,10 +461,12 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
410 | Rest.Log.DebugFormat("{0} Rest asset: {1} {2} {3}", | 461 | Rest.Log.DebugFormat("{0} Rest asset: {1} {2} {3}", |
411 | MsgId, asset.ID, asset.Type, asset.Name); | 462 | MsgId, asset.ID, asset.Type, asset.Name); |
412 | Rest.AssetServices.AddAsset(asset); | 463 | Rest.AssetServices.AddAsset(asset); |
464 | |||
413 | if (Rest.DumpAsset) | 465 | if (Rest.DumpAsset) |
414 | { | 466 | { |
415 | Rest.Dump(asset.Data); | 467 | Rest.Dump(asset.Data); |
416 | } | 468 | } |
469 | |||
417 | } | 470 | } |
418 | 471 | ||
419 | } | 472 | } |
@@ -424,11 +477,11 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
424 | foreach (InventoryFolderBase folder in entity.Folders) | 477 | foreach (InventoryFolderBase folder in entity.Folders) |
425 | { | 478 | { |
426 | 479 | ||
427 | InventoryFolderBase found = null; | 480 | InventoryFolderBase found; |
428 | 481 | ||
429 | // If the parentID is zero, then this is going | 482 | // If the parentID is zero, then this folder is going |
430 | // into the root identified by the URI. The requestor | 483 | // into the root folder identified by the URI. The requestor |
431 | // may have already set the parent ID correctly, in which | 484 | // may have already set the parent ID explicitly, in which |
432 | // case we don't have to do it here. | 485 | // case we don't have to do it here. |
433 | 486 | ||
434 | if (folder.ParentID == LLUUID.Zero) | 487 | if (folder.ParentID == LLUUID.Zero) |
@@ -437,7 +490,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
437 | } | 490 | } |
438 | 491 | ||
439 | // Search the existing inventory for an existing entry. If | 492 | // Search the existing inventory for an existing entry. If |
440 | // we have once, we need to decide if it has really changed. | 493 | // we have one, we need to decide if it has really changed. |
441 | // It could just be present as (unnecessary) context, and we | 494 | // It could just be present as (unnecessary) context, and we |
442 | // don't want to waste time updating the database in that | 495 | // don't want to waste time updating the database in that |
443 | // case, OR, it could be being moved from another location | 496 | // case, OR, it could be being moved from another location |
@@ -451,6 +504,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
451 | if (xf.ID == folder.ID) | 504 | if (xf.ID == folder.ID) |
452 | { | 505 | { |
453 | found = xf; | 506 | found = xf; |
507 | break; | ||
454 | } | 508 | } |
455 | } | 509 | } |
456 | 510 | ||
@@ -492,6 +546,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
492 | if (xi.ID == item.ID) | 546 | if (xi.ID == item.ID) |
493 | { | 547 | { |
494 | found = xi; | 548 | found = xi; |
549 | break; | ||
495 | } | 550 | } |
496 | } | 551 | } |
497 | 552 | ||
@@ -516,7 +571,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
516 | Rest.Log.DebugFormat("{0} {1}: Resource {2} is not a valid context: {3}", | 571 | Rest.Log.DebugFormat("{0} {1}: Resource {2} is not a valid context: {3}", |
517 | MsgId, rdata.method, rdata.path, InventoryNode.GetType()); | 572 | MsgId, rdata.method, rdata.path, InventoryNode.GetType()); |
518 | rdata.Fail(Rest.HttpStatusCodeBadRequest, | 573 | rdata.Fail(Rest.HttpStatusCodeBadRequest, |
519 | Rest.HttpStatusDescBadRequest); | 574 | Rest.HttpStatusDescBadRequest+": invalid resource context"); |
520 | } | 575 | } |
521 | 576 | ||
522 | rdata.Complete(); | 577 | rdata.Complete(); |
@@ -531,7 +586,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
531 | /// [1] It identifies the user whose inventory is to be | 586 | /// [1] It identifies the user whose inventory is to be |
532 | /// processed. | 587 | /// processed. |
533 | /// [2] It optionally specifies a subtree of the inventory | 588 | /// [2] It optionally specifies a subtree of the inventory |
534 | /// that is to be used to resolve an relative subtree | 589 | /// that is to be used to resolve any relative subtree |
535 | /// specifications in the entity. If nothing is specified | 590 | /// specifications in the entity. If nothing is specified |
536 | /// then the whole inventory is implied. | 591 | /// then the whole inventory is implied. |
537 | /// Please note that the subtree specified by the URI is only relevant | 592 | /// Please note that the subtree specified by the URI is only relevant |
@@ -540,7 +595,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
540 | /// elements will be implicitly referenced within the context identified | 595 | /// elements will be implicitly referenced within the context identified |
541 | /// by the URI. | 596 | /// by the URI. |
542 | /// If an element in the entity specifies an explicit parent folder, then | 597 | /// If an element in the entity specifies an explicit parent folder, then |
543 | /// that parent is effective, regardless of nay value specified in the | 598 | /// that parent is effective, regardless of any value specified in the |
544 | /// URI. If the parent does not exist, then the element, and any dependent | 599 | /// URI. If the parent does not exist, then the element, and any dependent |
545 | /// elements, are ignored. This case is actually detected and handled | 600 | /// elements, are ignored. This case is actually detected and handled |
546 | /// during the reconstitution process. | 601 | /// during the reconstitution process. |
@@ -555,33 +610,54 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
555 | 610 | ||
556 | Object InventoryNode = getInventoryNode(rdata, rdata.root, 1); | 611 | Object InventoryNode = getInventoryNode(rdata, rdata.root, 1); |
557 | 612 | ||
558 | // As long as we have a context, then we have something | 613 | // As long as we have a node, then we have something |
559 | // meaningful to do, unlike PUT. So reconstitute the | 614 | // meaningful to do, unlike PUT. So we reconstitute the |
560 | // subtree before doing anything else. Note that we | 615 | // subtree before doing anything else. Note that we |
561 | // etiher got a context or we threw an exception. | 616 | // etiher got a valid node or we threw an exception. |
562 | 617 | ||
563 | XmlInventoryCollection entity = ReconstituteEntity(rdata); | 618 | XmlInventoryCollection entity = ReconstituteEntity(rdata); |
564 | 619 | ||
565 | // Incorporate any inlined assets first | 620 | // Incorporate any inlined assets first. Any failures |
621 | // will terminate the request. | ||
566 | 622 | ||
567 | if (entity.Assets.Count != 0) | 623 | if (entity.Assets.Count > 0) |
568 | { | 624 | { |
625 | Rest.Log.DebugFormat("{0} Adding {1} assets to server", | ||
626 | MsgId, entity.Assets.Count); | ||
627 | |||
569 | foreach (AssetBase asset in entity.Assets) | 628 | foreach (AssetBase asset in entity.Assets) |
570 | { | 629 | { |
571 | // Asset was validated during the collection | 630 | Rest.Log.DebugFormat("{0} Rest asset: {1} {2} {3}", |
572 | // process | 631 | MsgId, asset.ID, asset.Type, asset.Name); |
632 | |||
633 | // The asset was validated during the collection process | ||
634 | |||
573 | Rest.AssetServices.AddAsset(asset); | 635 | Rest.AssetServices.AddAsset(asset); |
636 | |||
637 | if (Rest.DumpAsset) | ||
638 | { | ||
639 | Rest.Dump(asset.Data); | ||
640 | } | ||
641 | |||
574 | } | 642 | } |
575 | } | 643 | } |
576 | 644 | ||
577 | /// <summary> | 645 | /// <summary> |
578 | /// URI specifies a folder to be updated. | 646 | /// The URI specifies either a folder or an item to be updated. |
579 | /// </summary> | 647 | /// </summary> |
580 | /// <remarks> | 648 | /// <remarks> |
581 | /// The root node in the entity must have the same | 649 | /// The root node in the entity will replace the node identified |
582 | /// UUID as the node identified by the URI. The | 650 | /// by the URI. This means the parent will remain the same, but |
583 | /// parentID if different indicates that the updated | 651 | /// any or all attributes associated with the named element |
584 | /// folder is actually being moved too. | 652 | /// will change. |
653 | /// | ||
654 | /// If the inventory collection contains an element with a zero | ||
655 | /// parent ID, then this is taken to be the replacement for the | ||
656 | /// named node. The collection MAY also specify an explicit | ||
657 | /// parent ID, in this case it MAY identify the same parent as | ||
658 | /// the current node, or it MAY specify a different parent, | ||
659 | /// indicating that the folder is being moved in addition to any | ||
660 | /// other modifications being made. | ||
585 | /// </remarks> | 661 | /// </remarks> |
586 | 662 | ||
587 | if (typeof(InventoryFolderBase) == InventoryNode.GetType() || | 663 | if (typeof(InventoryFolderBase) == InventoryNode.GetType() || |
@@ -592,7 +668,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
592 | InventoryFolderBase xml = null; | 668 | InventoryFolderBase xml = null; |
593 | 669 | ||
594 | // Scan the set of folders in the entity collection for an | 670 | // Scan the set of folders in the entity collection for an |
595 | // entry that macthes the context folder. It is assumed that | 671 | // entry that matches the context folder. It is assumed that |
596 | // the only reliable indicator of this is a zero UUID ( using | 672 | // the only reliable indicator of this is a zero UUID ( using |
597 | // implicit context), or the parent's UUID matches that of the | 673 | // implicit context), or the parent's UUID matches that of the |
598 | // URI designated node (explicit context). We don't allow | 674 | // URI designated node (explicit context). We don't allow |
@@ -617,14 +693,15 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
617 | } | 693 | } |
618 | } | 694 | } |
619 | 695 | ||
620 | // More than one entry is ambiguous | 696 | // More than one entry is ambiguous. Other folders should be |
697 | // added using the PUT verb. | ||
621 | 698 | ||
622 | if (count > 1) | 699 | if (count > 1) |
623 | { | 700 | { |
624 | Rest.Log.DebugFormat("{0} {1}: Request for <{2}> is ambiguous", | 701 | Rest.Log.DebugFormat("{0} {1}: Request for <{2}> is ambiguous", |
625 | MsgId, rdata.method, rdata.path); | 702 | MsgId, rdata.method, rdata.path); |
626 | rdata.Fail(Rest.HttpStatusCodeBadRequest, | 703 | rdata.Fail(Rest.HttpStatusCodeBadRequest, |
627 | Rest.HttpStatusDescBadRequest); | 704 | Rest.HttpStatusDescBadRequest+": context is ambiguous"); |
628 | } | 705 | } |
629 | 706 | ||
630 | // Exactly one entry means we ARE replacing the node | 707 | // Exactly one entry means we ARE replacing the node |
@@ -679,7 +756,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
679 | Rest.Log.DebugFormat("{0} {1}: Request should not contain any folders <{2}>", | 756 | Rest.Log.DebugFormat("{0} {1}: Request should not contain any folders <{2}>", |
680 | MsgId, rdata.method, rdata.path); | 757 | MsgId, rdata.method, rdata.path); |
681 | rdata.Fail(Rest.HttpStatusCodeBadRequest, | 758 | rdata.Fail(Rest.HttpStatusCodeBadRequest, |
682 | Rest.HttpStatusDescBadRequest); | 759 | Rest.HttpStatusDescBadRequest+": folder is not allowed"); |
683 | } | 760 | } |
684 | 761 | ||
685 | if (entity.Items.Count > 1) | 762 | if (entity.Items.Count > 1) |
@@ -687,7 +764,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
687 | Rest.Log.DebugFormat("{0} {1}: Entity contains too many items <{2}>", | 764 | Rest.Log.DebugFormat("{0} {1}: Entity contains too many items <{2}>", |
688 | MsgId, rdata.method, rdata.path); | 765 | MsgId, rdata.method, rdata.path); |
689 | rdata.Fail(Rest.HttpStatusCodeBadRequest, | 766 | rdata.Fail(Rest.HttpStatusCodeBadRequest, |
690 | Rest.HttpStatusDescBadRequest); | 767 | Rest.HttpStatusDescBadRequest+": too may items"); |
691 | } | 768 | } |
692 | 769 | ||
693 | xml = entity.Items[0]; | 770 | xml = entity.Items[0]; |
@@ -854,7 +931,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
854 | { | 931 | { |
855 | Rest.Log.DebugFormat("{0} {1}: Request for {2} is ambiguous", | 932 | Rest.Log.DebugFormat("{0} {1}: Request for {2} is ambiguous", |
856 | MsgId, rdata.method, rdata.path); | 933 | MsgId, rdata.method, rdata.path); |
857 | rdata.Fail(Rest.HttpStatusCodeNotFound, Rest.HttpStatusDescNotFound); | 934 | rdata.Fail(Rest.HttpStatusCodeNotFound, Rest.HttpStatusDescNotFound+": request is ambiguous"); |
858 | } | 935 | } |
859 | } | 936 | } |
860 | } | 937 | } |
@@ -863,7 +940,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
863 | 940 | ||
864 | Rest.Log.DebugFormat("{0} {1}: Resource {2} not found", | 941 | Rest.Log.DebugFormat("{0} {1}: Resource {2} not found", |
865 | MsgId, rdata.method, rdata.path); | 942 | MsgId, rdata.method, rdata.path); |
866 | rdata.Fail(Rest.HttpStatusCodeNotFound, Rest.HttpStatusDescNotFound); | 943 | rdata.Fail(Rest.HttpStatusCodeNotFound, Rest.HttpStatusDescNotFound+": resource "+rdata.path+" not found"); |
867 | 944 | ||
868 | return null; /* Never reached */ | 945 | return null; /* Never reached */ |
869 | 946 | ||
@@ -931,7 +1008,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
931 | 1008 | ||
932 | Rest.Log.DebugFormat("{0} Inventory does not contain item/folder: <{1}>", | 1009 | Rest.Log.DebugFormat("{0} Inventory does not contain item/folder: <{1}>", |
933 | MsgId, rdata.path); | 1010 | MsgId, rdata.path); |
934 | rdata.Fail(Rest.HttpStatusCodeNotFound,Rest.HttpStatusDescNotFound); | 1011 | rdata.Fail(Rest.HttpStatusCodeNotFound,Rest.HttpStatusDescNotFound+": no such item/folder"); |
935 | 1012 | ||
936 | } | 1013 | } |
937 | 1014 | ||
@@ -1061,6 +1138,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
1061 | TrashCan.Version = 1; | 1138 | TrashCan.Version = 1; |
1062 | TrashCan.Type = (short) AssetType.TrashFolder; | 1139 | TrashCan.Type = (short) AssetType.TrashFolder; |
1063 | TrashCan.ParentID = f.ID; | 1140 | TrashCan.ParentID = f.ID; |
1141 | TrashCan.Owner = f.Owner; | ||
1064 | Rest.InventoryServices.AddFolder(TrashCan); | 1142 | Rest.InventoryServices.AddFolder(TrashCan); |
1065 | } | 1143 | } |
1066 | } | 1144 | } |
@@ -1070,7 +1148,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
1070 | { | 1148 | { |
1071 | Rest.Log.DebugFormat("{0} No Trash Can available", MsgId); | 1149 | Rest.Log.DebugFormat("{0} No Trash Can available", MsgId); |
1072 | rdata.Fail(Rest.HttpStatusCodeServerError, | 1150 | rdata.Fail(Rest.HttpStatusCodeServerError, |
1073 | Rest.HttpStatusDescServerError); | 1151 | Rest.HttpStatusDescServerError+": unable to create trash can"); |
1074 | } | 1152 | } |
1075 | 1153 | ||
1076 | return TrashCan; | 1154 | return TrashCan; |
@@ -1313,7 +1391,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
1313 | Rest.Log.DebugFormat("{0} Folder: unrecognized attribute: {1}:{2}", | 1391 | Rest.Log.DebugFormat("{0} Folder: unrecognized attribute: {1}:{2}", |
1314 | MsgId, ic.xml.Name, ic.xml.Value); | 1392 | MsgId, ic.xml.Name, ic.xml.Value); |
1315 | ic.Fail(Rest.HttpStatusCodeBadRequest, | 1393 | ic.Fail(Rest.HttpStatusCodeBadRequest, |
1316 | Rest.HttpStatusDescBadRequest); | 1394 | Rest.HttpStatusDescBadRequest+": unrecognized attribute"); |
1317 | break; | 1395 | break; |
1318 | } | 1396 | } |
1319 | } | 1397 | } |
@@ -1349,7 +1427,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
1349 | Rest.Log.ErrorFormat("{0} Invalid parent ID ({1}) in folder {2}", | 1427 | Rest.Log.ErrorFormat("{0} Invalid parent ID ({1}) in folder {2}", |
1350 | MsgId, ic.Item.Folder, result.ID); | 1428 | MsgId, ic.Item.Folder, result.ID); |
1351 | ic.Fail(Rest.HttpStatusCodeBadRequest, | 1429 | ic.Fail(Rest.HttpStatusCodeBadRequest, |
1352 | Rest.HttpStatusDescBadRequest); | 1430 | Rest.HttpStatusDescBadRequest+": invalid parent"); |
1353 | } | 1431 | } |
1354 | 1432 | ||
1355 | } | 1433 | } |
@@ -1457,7 +1535,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
1457 | Rest.Log.DebugFormat("{0} Item: Unrecognized attribute: {1}:{2}", | 1535 | Rest.Log.DebugFormat("{0} Item: Unrecognized attribute: {1}:{2}", |
1458 | MsgId, ic.xml.Name, ic.xml.Value); | 1536 | MsgId, ic.xml.Name, ic.xml.Value); |
1459 | ic.Fail(Rest.HttpStatusCodeBadRequest, | 1537 | ic.Fail(Rest.HttpStatusCodeBadRequest, |
1460 | Rest.HttpStatusDescBadRequest); | 1538 | Rest.HttpStatusDescBadRequest+": unrecognized attribute"); |
1461 | break; | 1539 | break; |
1462 | } | 1540 | } |
1463 | } | 1541 | } |
@@ -1570,7 +1648,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
1570 | { | 1648 | { |
1571 | Rest.Log.DebugFormat("{0} LLUID unimbedded asset must be inline", MsgId); | 1649 | Rest.Log.DebugFormat("{0} LLUID unimbedded asset must be inline", MsgId); |
1572 | ic.Fail(Rest.HttpStatusCodeBadRequest, | 1650 | ic.Fail(Rest.HttpStatusCodeBadRequest, |
1573 | Rest.HttpStatusDescBadRequest); | 1651 | Rest.HttpStatusDescBadRequest+": no context for asset"); |
1574 | } | 1652 | } |
1575 | } | 1653 | } |
1576 | 1654 | ||
@@ -1691,7 +1769,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
1691 | { | 1769 | { |
1692 | Rest.Log.ErrorFormat("{0} Unable to parse request", MsgId); | 1770 | Rest.Log.ErrorFormat("{0} Unable to parse request", MsgId); |
1693 | ic.Fail(Rest.HttpStatusCodeBadRequest, | 1771 | ic.Fail(Rest.HttpStatusCodeBadRequest, |
1694 | Rest.HttpStatusDescBadRequest); | 1772 | Rest.HttpStatusDescBadRequest+": request parse error"); |
1695 | } | 1773 | } |
1696 | 1774 | ||
1697 | // Every item is required to have a name (via REST anyway) | 1775 | // Every item is required to have a name (via REST anyway) |
@@ -1700,7 +1778,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
1700 | { | 1778 | { |
1701 | Rest.Log.ErrorFormat("{0} An item name MUST be specified", MsgId); | 1779 | Rest.Log.ErrorFormat("{0} An item name MUST be specified", MsgId); |
1702 | ic.Fail(Rest.HttpStatusCodeBadRequest, | 1780 | ic.Fail(Rest.HttpStatusCodeBadRequest, |
1703 | Rest.HttpStatusDescBadRequest); | 1781 | Rest.HttpStatusDescBadRequest+": item name required"); |
1704 | } | 1782 | } |
1705 | 1783 | ||
1706 | // An item MUST have an asset ID. AssetID should never be zero | 1784 | // An item MUST have an asset ID. AssetID should never be zero |
@@ -1713,7 +1791,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
1713 | Rest.Log.ErrorFormat("{0} Unable to complete request", MsgId); | 1791 | Rest.Log.ErrorFormat("{0} Unable to complete request", MsgId); |
1714 | Rest.Log.InfoFormat("{0} Asset information is missing", MsgId); | 1792 | Rest.Log.InfoFormat("{0} Asset information is missing", MsgId); |
1715 | ic.Fail(Rest.HttpStatusCodeBadRequest, | 1793 | ic.Fail(Rest.HttpStatusCodeBadRequest, |
1716 | Rest.HttpStatusDescBadRequest); | 1794 | Rest.HttpStatusDescBadRequest+": asset information required"); |
1717 | 1795 | ||
1718 | } | 1796 | } |
1719 | 1797 | ||
@@ -1751,7 +1829,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
1751 | Rest.Log.ErrorFormat("{0} Invalid parent ID ({1}) in item {2}", | 1829 | Rest.Log.ErrorFormat("{0} Invalid parent ID ({1}) in item {2}", |
1752 | MsgId, ic.Item.Folder, ic.Item.ID); | 1830 | MsgId, ic.Item.Folder, ic.Item.ID); |
1753 | ic.Fail(Rest.HttpStatusCodeBadRequest, | 1831 | ic.Fail(Rest.HttpStatusCodeBadRequest, |
1754 | Rest.HttpStatusDescBadRequest); | 1832 | Rest.HttpStatusDescBadRequest+": parent information required"); |
1755 | } | 1833 | } |
1756 | 1834 | ||
1757 | } | 1835 | } |
@@ -1825,6 +1903,22 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
1825 | if (ic.Item.InvType == (int) AssetType.Unknown) | 1903 | if (ic.Item.InvType == (int) AssetType.Unknown) |
1826 | ic.Item.InvType = (int) AssetType.ImageJPEG; | 1904 | ic.Item.InvType = (int) AssetType.ImageJPEG; |
1827 | break; | 1905 | break; |
1906 | case "tga" : | ||
1907 | if (parts[parts.Length - 2].IndexOf("_texture") != -1) | ||
1908 | { | ||
1909 | if (ic.Item.AssetType == (int) AssetType.Unknown) | ||
1910 | ic.Item.AssetType = (int) AssetType.TextureTGA; | ||
1911 | if (ic.Item.InvType == (int) AssetType.Unknown) | ||
1912 | ic.Item.InvType = (int) AssetType.TextureTGA; | ||
1913 | } | ||
1914 | else | ||
1915 | { | ||
1916 | if (ic.Item.AssetType == (int) AssetType.Unknown) | ||
1917 | ic.Item.AssetType = (int) AssetType.ImageTGA; | ||
1918 | if (ic.Item.InvType == (int) AssetType.Unknown) | ||
1919 | ic.Item.InvType = (int) AssetType.ImageTGA; | ||
1920 | } | ||
1921 | break; | ||
1828 | default : | 1922 | default : |
1829 | Rest.Log.DebugFormat("{0} Type was not inferred", MsgId); | 1923 | Rest.Log.DebugFormat("{0} Type was not inferred", MsgId); |
1830 | break; | 1924 | break; |
@@ -1832,6 +1926,15 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
1832 | } | 1926 | } |
1833 | } | 1927 | } |
1834 | 1928 | ||
1929 | /// If this is a TGA remember the fact | ||
1930 | |||
1931 | if (ic.Item.AssetType == (int) AssetType.TextureTGA || | ||
1932 | ic.Item.AssetType == (int) AssetType.ImageTGA) | ||
1933 | { | ||
1934 | // TODO: DO we need to convert it? Or is it enough to flag | ||
1935 | // it appropriately? | ||
1936 | } | ||
1937 | |||
1835 | ic.reset(); | 1938 | ic.reset(); |
1836 | 1939 | ||
1837 | } | 1940 | } |