aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/ApplicationPlugins/Rest/Inventory/RestInventoryServices.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/ApplicationPlugins/Rest/Inventory/RestInventoryServices.cs')
-rw-r--r--OpenSim/ApplicationPlugins/Rest/Inventory/RestInventoryServices.cs916
1 files changed, 617 insertions, 299 deletions
diff --git a/OpenSim/ApplicationPlugins/Rest/Inventory/RestInventoryServices.cs b/OpenSim/ApplicationPlugins/Rest/Inventory/RestInventoryServices.cs
index 8a0eba5..6a0fdf2 100644
--- a/OpenSim/ApplicationPlugins/Rest/Inventory/RestInventoryServices.cs
+++ b/OpenSim/ApplicationPlugins/Rest/Inventory/RestInventoryServices.cs
@@ -23,6 +23,7 @@
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 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 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. 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 *
26 */ 27 */
27 28
28using System; 29using System;
@@ -41,22 +42,30 @@ using Nini.Config;
41 42
42namespace OpenSim.ApplicationPlugins.Rest.Inventory 43namespace OpenSim.ApplicationPlugins.Rest.Inventory
43{ 44{
45
44 public class RestInventoryServices : IRest 46 public class RestInventoryServices : IRest
45 { 47 {
48
49 private static readonly int PARM_USERID = 0;
50 private static readonly int PARM_PATH = 1;
51
46 private bool enabled = false; 52 private bool enabled = false;
47 private string qPrefix = "inventory"; 53 private string qPrefix = "inventory";
48 54
55 private static readonly string PRIVATE_ROOT_NAME = "My Inventory";
56
49 /// <summary> 57 /// <summary>
50 /// A simple constructor is used to handle any once-only 58 /// The constructor makes sure that the service prefix is absolute
51 /// initialization of working classes. 59 /// and the registers the service handler and the allocator.
52 /// </summary> 60 /// </summary>
53 61
54 public RestInventoryServices() 62 public RestInventoryServices()
55 { 63 {
64
56 Rest.Log.InfoFormat("{0} Inventory services initializing", MsgId); 65 Rest.Log.InfoFormat("{0} Inventory services initializing", MsgId);
57 Rest.Log.InfoFormat("{0} Using REST Implementation Version {1}", MsgId, Rest.Version); 66 Rest.Log.InfoFormat("{0} Using REST Implementation Version {1}", MsgId, Rest.Version);
58 67
59 // If a relative path was specified for the handler's domain, 68 // If a relative path was specified for the handler's domain,
60 // add the standard prefix to make it absolute, e.g. /admin 69 // add the standard prefix to make it absolute, e.g. /admin
61 70
62 if (!qPrefix.StartsWith(Rest.UrlPathSeparator)) 71 if (!qPrefix.StartsWith(Rest.UrlPathSeparator))
@@ -73,6 +82,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
73 enabled = true; 82 enabled = true;
74 83
75 Rest.Log.InfoFormat("{0} Inventory services initialization complete", MsgId); 84 Rest.Log.InfoFormat("{0} Inventory services initialization complete", MsgId);
85
76 } 86 }
77 87
78 /// <summary> 88 /// <summary>
@@ -85,9 +95,8 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
85 } 95 }
86 96
87 /// <summary> 97 /// <summary>
88 /// Called by the plug-in to halt REST processing. Local processing is 98 /// Called by the plug-in to halt service processing. Local processing is
89 /// disabled, and control blocks until all current processing has 99 /// disabled.
90 /// completed. No new processing will be started
91 /// </summary> 100 /// </summary>
92 101
93 public void Close() 102 public void Close()
@@ -114,6 +123,10 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
114 /// completes. All request-instance specific state is kept here. This 123 /// completes. All request-instance specific state is kept here. This
115 /// is registered when this service provider is registered. 124 /// is registered when this service provider is registered.
116 /// </summary> 125 /// </summary>
126 /// <param name=request>Inbound HTTP request information</param>
127 /// <param name=response>Outbound HTTP request information</param>
128 /// <param name=qPrefix>REST service domain prefix</param>
129 /// <returns>A RequestData instance suitable for this service</returns>
117 130
118 private RequestData Allocate(OSHttpRequest request, OSHttpResponse response) 131 private RequestData Allocate(OSHttpRequest request, OSHttpResponse response)
119 { 132 {
@@ -123,12 +136,14 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
123 /// <summary> 136 /// <summary>
124 /// This method is registered with the handler when this service provider 137 /// This method is registered with the handler when this service provider
125 /// is initialized. It is called whenever the plug-in identifies this service 138 /// is initialized. It is called whenever the plug-in identifies this service
126 /// provider as the best match. 139 /// provider as the best match for a given request.
127 /// It handles all aspects of inventory REST processing. 140 /// It handles all aspects of inventory REST processing, i.e. /admin/inventory
128 /// </summary> 141 /// </summary>
142 /// <param name=hdata>A consolidated HTTP request work area</param>
129 143
130 private void DoInventory(RequestData hdata) 144 private void DoInventory(RequestData hdata)
131 { 145 {
146
132 InventoryRequestData rdata = (InventoryRequestData) hdata; 147 InventoryRequestData rdata = (InventoryRequestData) hdata;
133 148
134 Rest.Log.DebugFormat("{0} DoInventory ENTRY", MsgId); 149 Rest.Log.DebugFormat("{0} DoInventory ENTRY", MsgId);
@@ -140,7 +155,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
140 return; 155 return;
141 } 156 }
142 157
143 // Now that we know this is a serious attempt to 158 // Now that we know this is a serious attempt to
144 // access inventory data, we should find out who 159 // access inventory data, we should find out who
145 // is asking, and make sure they are authorized 160 // is asking, and make sure they are authorized
146 // to do so. We need to validate the caller's 161 // to do so. We need to validate the caller's
@@ -151,14 +166,14 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
151 // With the present HTTP server we can't use the 166 // With the present HTTP server we can't use the
152 // builtin authentication mechanisms because they 167 // builtin authentication mechanisms because they
153 // would be enforced for all in-bound requests. 168 // would be enforced for all in-bound requests.
154 // Instead we look at the headers ourselves and 169 // Instead we look at the headers ourselves and
155 // handle authentication directly. 170 // handle authentication directly.
156 171
157 try 172 try
158 { 173 {
159 if (!rdata.IsAuthenticated) 174 if (!rdata.IsAuthenticated)
160 { 175 {
161 rdata.Fail(Rest.HttpStatusCodeNotAuthorized, Rest.HttpStatusDescNotAuthorized); 176 rdata.Fail(Rest.HttpStatusCodeNotAuthorized,String.Format("user \"{0}\" could not be authenticated", rdata.userName));
162 } 177 }
163 } 178 }
164 catch (RestException e) 179 catch (RestException e)
@@ -178,43 +193,43 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
178 193
179 Rest.Log.DebugFormat("{0} Authenticated {1}", MsgId, rdata.userName); 194 Rest.Log.DebugFormat("{0} Authenticated {1}", MsgId, rdata.userName);
180 195
181 /// <remarks> 196 // We can only get here if we are authorized
182 /// We can only get here if we are authorized 197 //
183 /// 198 // The requestor may have specified an LLUUID or
184 /// The requestor may have specified an LLUUID or 199 // a conjoined FirstName LastName string. We'll
185 /// a conjoined FirstName LastName string. We'll 200 // try both. If we fail with the first, UUID,
186 /// try both. If we fail with the first, UUID, 201 // attempt, we try the other. As an example, the
187 /// attempt, we try the other. As an example, the 202 // URI for a valid inventory request might be:
188 /// URI for a valid inventory request might be: 203 //
189 /// 204 // http://<host>:<port>/admin/inventory/Arthur Dent
190 /// http://<host>:<port>/admin/inventory/Arthur Dent 205 //
191 /// 206 // Indicating that this is an inventory request for
192 /// Indicating that this is an inventory request for 207 // an avatar named Arthur Dent. This is ALL that is
193 /// an avatar named Arthur Dent. This is ALl that is 208 // required to designate a GET for an entire
194 /// required to designate a GET for an entire 209 // inventory.
195 /// inventory. 210 //
196 /// </remarks>
197
198 // Do we have at least a user agent name? 211 // Do we have at least a user agent name?
199 212
200 if (rdata.parameters.Length < 1) 213 if (rdata.Parameters.Length < 1)
201 { 214 {
202 Rest.Log.WarnFormat("{0} Inventory: No user agent identifier specified", MsgId); 215 Rest.Log.WarnFormat("{0} Inventory: No user agent identifier specified", MsgId);
203 rdata.Fail(Rest.HttpStatusCodeBadRequest, Rest.HttpStatusDescBadRequest+": No user identity specified"); 216 rdata.Fail(Rest.HttpStatusCodeBadRequest, "no user identity specified");
204 } 217 }
205 218
206 // The first parameter MUST be the agent identification, either an LLUUID 219 // The first parameter MUST be the agent identification, either an LLUUID
207 // or a space-separated First-name Last-Name specification. 220 // or a space-separated First-name Last-Name specification. We check for
221 // an LLUUID first, if anyone names their character using a valid LLUUID
222 // that identifies another existing avatar will cause this a problem...
208 223
209 try 224 try
210 { 225 {
211 rdata.uuid = new LLUUID(rdata.parameters[0]); 226 rdata.uuid = new LLUUID(rdata.Parameters[PARM_USERID]);
212 Rest.Log.DebugFormat("{0} LLUUID supplied", MsgId); 227 Rest.Log.DebugFormat("{0} LLUUID supplied", MsgId);
213 rdata.userProfile = Rest.UserServices.GetUserProfile(rdata.uuid); 228 rdata.userProfile = Rest.UserServices.GetUserProfile(rdata.uuid);
214 } 229 }
215 catch 230 catch
216 { 231 {
217 string[] names = rdata.parameters[0].Split(Rest.CA_SPACE); 232 string[] names = rdata.Parameters[PARM_USERID].Split(Rest.CA_SPACE);
218 if (names.Length == 2) 233 if (names.Length == 2)
219 { 234 {
220 Rest.Log.DebugFormat("{0} Agent Name supplied [2]", MsgId); 235 Rest.Log.DebugFormat("{0} Agent Name supplied [2]", MsgId);
@@ -222,23 +237,23 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
222 } 237 }
223 else 238 else
224 { 239 {
225 Rest.Log.DebugFormat("{0} A Valid UUID or both first and last names must be specified", MsgId); 240 Rest.Log.WarnFormat("{0} A Valid UUID or both first and last names must be specified", MsgId);
226 rdata.Fail(Rest.HttpStatusCodeBadRequest, Rest.HttpStatusDescBadRequest+": invalid user identity"); 241 rdata.Fail(Rest.HttpStatusCodeBadRequest, "invalid user identity");
227 } 242 }
228 } 243 }
229 244
230 // If the user rpofile is null then either the server is broken, or the 245 // If the user profile is null then either the server is broken, or the
231 // user is not known. We always assume the latter case. 246 // user is not known. We always assume the latter case.
232 247
233 if (rdata.userProfile != null) 248 if (rdata.userProfile != null)
234 { 249 {
235 Rest.Log.DebugFormat("{0} Profile obtained for agent {1} {2}", 250 Rest.Log.DebugFormat("{0} Profile obtained for agent {1} {2}",
236 MsgId, rdata.userProfile.FirstName, rdata.userProfile.SurName); 251 MsgId, rdata.userProfile.FirstName, rdata.userProfile.SurName);
237 } 252 }
238 else 253 else
239 { 254 {
240 Rest.Log.DebugFormat("{0} No profile for {1}", MsgId, rdata.path); 255 Rest.Log.WarnFormat("{0} No profile for {1}", MsgId, rdata.path);
241 rdata.Fail(Rest.HttpStatusCodeNotFound,Rest.HttpStatusDescNotFound+": unrecognized user identity"); 256 rdata.Fail(Rest.HttpStatusCodeNotFound, "unrecognized user identity");
242 } 257 }
243 258
244 // If we get to here, then we have effectively validated the user's 259 // If we get to here, then we have effectively validated the user's
@@ -254,17 +269,18 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
254 // response is not recieved in a timely fashion. 269 // response is not recieved in a timely fashion.
255 270
256 rdata.uuid = rdata.userProfile.ID; 271 rdata.uuid = rdata.userProfile.ID;
257 272
258 if (Rest.InventoryServices.HasInventoryForUser(rdata.uuid)) 273 if (Rest.InventoryServices.HasInventoryForUser(rdata.uuid))
259 { 274 {
275
260 rdata.root = Rest.InventoryServices.RequestRootFolder(rdata.uuid); 276 rdata.root = Rest.InventoryServices.RequestRootFolder(rdata.uuid);
261 277
262 Rest.Log.DebugFormat("{0} Inventory Root retrieved for {1} {2}", 278 Rest.Log.DebugFormat("{0} Inventory Root retrieved for {1} {2}",
263 MsgId, rdata.userProfile.FirstName, rdata.userProfile.SurName); 279 MsgId, rdata.userProfile.FirstName, rdata.userProfile.SurName);
264 280
265 Rest.InventoryServices.RequestInventoryForUser(rdata.uuid, rdata.GetUserInventory); 281 Rest.InventoryServices.RequestInventoryForUser(rdata.uuid, rdata.GetUserInventory);
266 282
267 Rest.Log.DebugFormat("{0} Inventory catalog requested for {1} {2}", 283 Rest.Log.DebugFormat("{0} Inventory catalog requested for {1} {2}",
268 MsgId, rdata.userProfile.FirstName, rdata.userProfile.SurName); 284 MsgId, rdata.userProfile.FirstName, rdata.userProfile.SurName);
269 285
270 lock (rdata) 286 lock (rdata)
@@ -277,16 +293,17 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
277 293
278 if (rdata.root == null) 294 if (rdata.root == null)
279 { 295 {
280 Rest.Log.DebugFormat("{0} Inventory is not available [1] for agent {1} {2}", 296 Rest.Log.WarnFormat("{0} Inventory is not available [1] for agent {1} {2}",
281 MsgId, rdata.userProfile.FirstName, rdata.userProfile.SurName); 297 MsgId, rdata.userProfile.FirstName, rdata.userProfile.SurName);
282 rdata.Fail(Rest.HttpStatusCodeServerError,Rest.HttpStatusDescServerError+": inventory retrieval failed"); 298 rdata.Fail(Rest.HttpStatusCodeServerError, "inventory retrieval failed");
283 } 299 }
300
284 } 301 }
285 else 302 else
286 { 303 {
287 Rest.Log.DebugFormat("{0} Inventory is not available for agent [3] {1} {2}", 304 Rest.Log.WarnFormat("{0} Inventory is not locally available for agent {1} {2}",
288 MsgId, rdata.userProfile.FirstName, rdata.userProfile.SurName); 305 MsgId, rdata.userProfile.FirstName, rdata.userProfile.SurName);
289 rdata.Fail(Rest.HttpStatusCodeNotFound,Rest.HttpStatusDescNotFound+": no inventory for user"); 306 rdata.Fail(Rest.HttpStatusCodeNotFound, "no local inventory for user");
290 } 307 }
291 308
292 // If we get here, then we have successfully retrieved the user's information 309 // If we get here, then we have successfully retrieved the user's information
@@ -294,34 +311,35 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
294 311
295 switch (rdata.method) 312 switch (rdata.method)
296 { 313 {
297 case Rest.HEAD : // Do the processing, set the status code, suppress entity
298 DoGet(rdata);
299 rdata.buffer = null;
300 break;
301 314
302 case Rest.GET : // Do the processing, set the status code, return entity 315 case Rest.HEAD : // Do the processing, set the status code, suppress entity
303 DoGet(rdata); 316 DoGet(rdata);
304 break; 317 rdata.buffer = null;
318 break;
305 319
306 case Rest.PUT : // Add new information 320 case Rest.GET : // Do the processing, set the status code, return entity
307 DoPut(rdata); 321 DoGet(rdata);
308 break; 322 break;
309 323
310 case Rest.POST : // Update (replace) 324 case Rest.PUT : // Update named element
311 DoPost(rdata); 325 DoUpdate(rdata);
312 break; 326 break;
313 327
314 case Rest.DELETE : // Delete information 328 case Rest.POST : // Add new information to identified context.
315 DoDelete(rdata); 329 DoExtend(rdata);
316 break; 330 break;
317 331
318 default : 332 case Rest.DELETE : // Delete information
319 Rest.Log.DebugFormat("{0} Method {1} not supported for {2}", 333 DoDelete(rdata);
320 MsgId, rdata.method, rdata.path); 334 break;
321 rdata.Fail(Rest.HttpStatusCodeMethodNotAllowed, 335
322 Rest.HttpStatusDescMethodNotAllowed+": "+rdata.method+" not supported"); 336 default :
323 break; 337 Rest.Log.WarnFormat("{0} Method {1} not supported for {2}",
338 MsgId, rdata.method, rdata.path);
339 rdata.Fail(Rest.HttpStatusCodeMethodNotAllowed, rdata.method+" not supported");
340 break;
324 } 341 }
342
325 } 343 }
326 344
327 #endregion Interface 345 #endregion Interface
@@ -333,84 +351,97 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
333 /// Any remaining parameters are used to locate the 351 /// Any remaining parameters are used to locate the
334 /// corresponding subtree based upon node name. 352 /// corresponding subtree based upon node name.
335 /// </summary> 353 /// </summary>
354 /// <param name=rdata>HTTP service request work area</param>
336 355
337 private void DoGet(InventoryRequestData rdata) 356 private void DoGet(InventoryRequestData rdata)
338 { 357 {
358
339 rdata.initXmlWriter(); 359 rdata.initXmlWriter();
340 360
341 rdata.writer.WriteStartElement(String.Empty,"Inventory",String.Empty); 361 rdata.writer.WriteStartElement(String.Empty,"Inventory",String.Empty);
342 362
343 // If there was only one parameter, then the entire
344 // inventory is being requested.
345
346 if (rdata.parameters.Length == 1)
347 {
348 formatInventory(rdata, rdata.root, String.Empty);
349 }
350
351 // If there are additional parameters, then these represent 363 // If there are additional parameters, then these represent
352 // a path relative to the root of the inventory. This path 364 // a path relative to the root of the inventory. This path
353 // must be traversed before we format the sub-tree thus 365 // must be traversed before we format the sub-tree thus
354 // identified. 366 // identified.
355 367
356 else 368 traverse(rdata, rdata.root, PARM_PATH);
357 { 369
358 traverseInventory(rdata, rdata.root, 1); 370 // Close all open elements
359 }
360 371
361 rdata.writer.WriteFullEndElement(); 372 rdata.writer.WriteFullEndElement();
362 373
374 // Indicate a successful request
375
363 rdata.Complete(); 376 rdata.Complete();
364 rdata.Respond("Inventory " + rdata.method + ": Normal completion");
365 }
366 377
378 // Send the response to the user. The body will be implicitly
379 // constructed from the result of the XML writer.
380
381 rdata.Respond(String.Format("Inventory {0} Normal completion", rdata.method));
382
383 }
384
367 /// <summary> 385 /// <summary>
368 /// In the case of the inventory, and probably in general, 386 /// In the case of the inventory, and probably in general,
369 /// the distinction between PUT and POST is not always 387 /// the distinction between PUT and POST is not always
370 /// easy to discern. Adding a directory can be viewed as 388 /// easy to discern. The standard is badly worded in places,
389 /// and adding a node to a hierarchy can be viewed as
371 /// an addition, or as a modification to the inventory as 390 /// an addition, or as a modification to the inventory as
372 /// a whole. This is exacerbated by a lack of consistency 391 /// a whole. This is exacerbated by an unjustified lack of
373 /// across different implementations. 392 /// consistency across different implementations.
374 /// 393 ///
375 /// For OpenSim POST is an update and PUT is an addition. 394 /// For OpenSim PUT is an update and POST is an addition. This
395 /// is the behavior required by the HTTP specification and
396 /// therefore as required by REST.
376 /// 397 ///
377 /// The best way to exaplain the distinction is to 398 /// The best way to explain the distinction is to
378 /// consider the relationship between the URI and the 399 /// consider the relationship between the URI and the
379 /// entity in question. For POST, the URI identifies the 400 /// enclosed entity. For PUT, the URI identifies the
380 /// entity to be modified or replaced. 401 /// actual entity to be modified or replaced, i.e. the
381 /// If the operation is PUT,then the URI describes the 402 /// enclosed entity.
403 ///
404 /// If the operation is POST,then the URI describes the
382 /// context into which the new entity will be added. 405 /// context into which the new entity will be added.
383 /// 406 ///
384 /// As an example, suppose the URI contains: 407 /// As an example, suppose the URI contains:
385 /// /admin/inventory/Clothing 408 /// /admin/inventory/Clothing
386 /// 409 ///
387 /// A POST request will result in some modification of 410 /// A PUT request will normally result in some modification of
388 /// the folder or item named "Clothing". Whereas a PUT 411 /// the folder or item named "Clothing". Whereas a POST
389 /// request will add some new information into the 412 /// request will normally add some new information into the
390 /// content identified by Clothing. It follows from this 413 /// content identified by Clothing. It follows from this
391 /// that for PUT, the element identified by the URI must 414 /// that for POST, the element identified by the URI MUST
392 /// be a folder. 415 /// be a folder.
393 /// </summary> 416 /// </summary>
394 417
395 /// <summary> 418 /// <summary>
396 /// PUT adds new information to the inventory in the 419 /// POST adds new information to the inventory in the
397 /// context identified by the URI. 420 /// context identified by the URI.
398 /// </summary> 421 /// </summary>
422 /// <param name=rdata>HTTP service request work area</param>
399 423
400 private void DoPut(InventoryRequestData rdata) 424 private void DoExtend(InventoryRequestData rdata)
401 { 425 {
426
427 bool created = false;
428 bool modified = false;
429 string newnode = String.Empty;
430
402 // Resolve the context node specified in the URI. Entity 431 // Resolve the context node specified in the URI. Entity
403 // data will be ADDED beneath this node. 432 // data will be ADDED beneath this node. rdata already contains
433 // information about the current content of the user's
434 // inventory.
404 435
405 Object InventoryNode = getInventoryNode(rdata, rdata.root, 1); 436 Object InventoryNode = getInventoryNode(rdata, rdata.root, PARM_PATH, Rest.Fill);
406 437
407 // Processing depends upon the type of inventory node 438 // Processing depends upon the type of inventory node
408 // identified in the URI. This is the CONTEXT for the 439 // identified in the URI. This is the CONTEXT for the
409 // change. We either got a context or we threw an 440 // change. We either got a context or we threw an
410 // exception. 441 // exception.
411 442
412 // It follows that we can only add information if the URI 443 // It follows that we can only add information if the URI
413 // has identified a folder. So only a type of folder is supported 444 // has identified a folder. So only a type of folder is supported
414 // in this case. 445 // in this case.
415 446
416 if (typeof(InventoryFolderBase) == InventoryNode.GetType() || 447 if (typeof(InventoryFolderBase) == InventoryNode.GetType() ||
@@ -430,16 +461,17 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
430 // [1] A (possibly empty) set of folders. 461 // [1] A (possibly empty) set of folders.
431 // [2] A (possibly empty) set of items. 462 // [2] A (possibly empty) set of items.
432 // [3] A (possibly empty) set of assets. 463 // [3] A (possibly empty) set of assets.
433 // If all of these are empty, then the PUT is a harmless no-operation. 464 // If all of these are empty, then the POST is a harmless no-operation.
434 465
435 XmlInventoryCollection entity = ReconstituteEntity(rdata); 466 XmlInventoryCollection entity = ReconstituteEntity(rdata);
436 467
437 // Inlined assets can be included in entity. These must be incorporated into 468 // Inlined assets can be included in entity. These must be incorporated into
438 // the asset database before we attempt to update the inventory. If anything 469 // the asset database before we attempt to update the inventory. If anything
439 // fails, return a failure to requestor. 470 // fails, return a failure to requestor.
440 471
441 if (entity.Assets.Count > 0) 472 if (entity.Assets.Count > 0)
442 { 473 {
474
443 Rest.Log.DebugFormat("{0} Adding {1} assets to server", 475 Rest.Log.DebugFormat("{0} Adding {1} assets to server",
444 MsgId, entity.Assets.Count); 476 MsgId, entity.Assets.Count);
445 477
@@ -449,11 +481,17 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
449 MsgId, asset.ID, asset.Type, asset.Name); 481 MsgId, asset.ID, asset.Type, asset.Name);
450 Rest.AssetServices.AddAsset(asset); 482 Rest.AssetServices.AddAsset(asset);
451 483
452 if (Rest.DumpAsset) 484 created = true;
485 rdata.appendStatus(String.Format("<p> Created asset {0}, UUID {1}<p>",
486 asset.Name, asset.ID));
487
488 if (Rest.DEBUG && Rest.DumpAsset)
453 { 489 {
454 Rest.Dump(asset.Data); 490 Rest.Dump(asset.Data);
455 } 491 }
492
456 } 493 }
494
457 } 495 }
458 496
459 // Modify the context using the collection of folders and items 497 // Modify the context using the collection of folders and items
@@ -461,6 +499,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
461 499
462 foreach (InventoryFolderBase folder in entity.Folders) 500 foreach (InventoryFolderBase folder in entity.Folders)
463 { 501 {
502
464 InventoryFolderBase found; 503 InventoryFolderBase found;
465 504
466 // If the parentID is zero, then this folder is going 505 // If the parentID is zero, then this folder is going
@@ -468,9 +507,15 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
468 // may have already set the parent ID explicitly, in which 507 // may have already set the parent ID explicitly, in which
469 // case we don't have to do it here. 508 // case we don't have to do it here.
470 509
471 if (folder.ParentID == LLUUID.Zero) 510 if (folder.ParentID == LLUUID.Zero || folder.ParentID == context.ID)
472 { 511 {
512 if (newnode != String.Empty)
513 {
514 Rest.Log.DebugFormat("{0} Too many resources", MsgId);
515 rdata.Fail(Rest.HttpStatusCodeBadRequest, "only one root entity is allowed");
516 }
473 folder.ParentID = context.ID; 517 folder.ParentID = context.ID;
518 newnode = folder.Name;
474 } 519 }
475 520
476 // Search the existing inventory for an existing entry. If 521 // Search the existing inventory for an existing entry. If
@@ -496,12 +541,22 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
496 { 541 {
497 Rest.Log.DebugFormat("{0} Updating existing folder", MsgId); 542 Rest.Log.DebugFormat("{0} Updating existing folder", MsgId);
498 Rest.InventoryServices.MoveFolder(folder); 543 Rest.InventoryServices.MoveFolder(folder);
544
545 modified = true;
546 rdata.appendStatus(String.Format("<p> Created folder {0}, UUID {1}<p>",
547 folder.Name, folder.ID));
499 } 548 }
500 else 549 else
501 { 550 {
502 Rest.Log.DebugFormat("{0} Adding new folder", MsgId); 551 Rest.Log.DebugFormat("{0} Adding new folder", MsgId);
503 Rest.InventoryServices.AddFolder(folder); 552 Rest.InventoryServices.AddFolder(folder);
553
554 created = true;
555 rdata.appendStatus(String.Format("<p> Modified folder {0}, UUID {1}<p>",
556 folder.Name, folder.ID));
557
504 } 558 }
559
505 } 560 }
506 561
507 // Now we repeat a similar process for the items included 562 // Now we repeat a similar process for the items included
@@ -509,6 +564,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
509 564
510 foreach (InventoryItemBase item in entity.Items) 565 foreach (InventoryItemBase item in entity.Items)
511 { 566 {
567
512 InventoryItemBase found = null; 568 InventoryItemBase found = null;
513 569
514 // If the parentID is zero, then this is going 570 // If the parentID is zero, then this is going
@@ -519,7 +575,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
519 item.Folder = context.ID; 575 item.Folder = context.ID;
520 } 576 }
521 577
522 // Determine whether this is a new item or a 578 // Determine whether this is a new item or a
523 // replacement definition. 579 // replacement definition.
524 580
525 foreach (InventoryItemBase xi in rdata.items) 581 foreach (InventoryItemBase xi in rdata.items)
@@ -537,60 +593,90 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
537 Rest.Log.DebugFormat("{0} Updating item {1} {2} {3} {4} {5}", 593 Rest.Log.DebugFormat("{0} Updating item {1} {2} {3} {4} {5}",
538 MsgId, item.ID, item.AssetID, item.InvType, item.AssetType, item.Name); 594 MsgId, item.ID, item.AssetID, item.InvType, item.AssetType, item.Name);
539 Rest.InventoryServices.UpdateItem(item); 595 Rest.InventoryServices.UpdateItem(item);
596 modified = true;
597 rdata.appendStatus(String.Format("<p> Modified item {0}, UUID {1}<p>", item.Name, item.ID));
540 } 598 }
541 else 599 else
542 { 600 {
543 Rest.Log.DebugFormat("{0} Adding item {1} {2} {3} {4} {5}", 601 Rest.Log.DebugFormat("{0} Adding item {1} {2} {3} {4} {5}",
544 MsgId, item.ID, item.AssetID, item.InvType, item.AssetType, item.Name); 602 MsgId, item.ID, item.AssetID, item.InvType, item.AssetType, item.Name);
545 Rest.InventoryServices.AddItem(item); 603 Rest.InventoryServices.AddItem(item);
604 created = true;
605 rdata.appendStatus(String.Format("<p> Created item {2}, UUID {3}<p>", item.Name, item.ID));
546 } 606 }
607
547 } 608 }
609
610 if (created)
611 {
612 // Must include a location header with a URI that identifies the new resource.
613 rdata.AddHeader(Rest.HttpHeaderLocation,String.Format("http://{0}{1}/{2}",
614 rdata.hostname+":"+rdata.port,rdata.path,newnode));
615 rdata.Complete(Rest.HttpStatusCodeCreated);
616 }
617 else
618 {
619 if (modified)
620 {
621 rdata.Complete(Rest.HttpStatusCodeOK);
622 }
623 else
624 {
625 rdata.Complete(Rest.HttpStatusCodeNoContent);
626 }
627 }
628
629 rdata.Respond("Inventory " + rdata.method + ": Normal completion");
630
548 } 631 }
549 else 632 else
550 { 633 {
551 Rest.Log.DebugFormat("{0} {1}: Resource {2} is not a valid context: {3}", 634 Rest.Log.DebugFormat("{0} {1}: Resource {2} is not a valid context: {3}",
552 MsgId, rdata.method, rdata.path, InventoryNode.GetType()); 635 MsgId, rdata.method, rdata.path, InventoryNode.GetType());
553 rdata.Fail(Rest.HttpStatusCodeBadRequest, 636 rdata.Fail(Rest.HttpStatusCodeBadRequest, "invalid resource context");
554 Rest.HttpStatusDescBadRequest+": invalid resource context");
555 } 637 }
556 638
557 rdata.Complete();
558 rdata.Respond("Inventory " + rdata.method + ": Normal completion");
559 } 639 }
560 640
561 /// <summary> 641 /// <summary>
562 /// POST updates the URI-identified element in the inventory. This 642 /// PUT updates the URI-identified element in the inventory. This
563 /// is actually far more flexible than it might at first sound. For 643 /// is actually far more flexible than it might at first sound. For
564 /// POST the URI serves two purposes: 644 /// PUT the URI serves two purposes:
565 /// [1] It identifies the user whose inventory is to be 645 /// [1] It identifies the user whose inventory is to be
566 /// processed. 646 /// processed.
567 /// [2] It optionally specifies a subtree of the inventory 647 /// [2] It optionally specifies a subtree of the inventory
568 /// that is to be used to resolve any relative subtree 648 /// that is to be used to resolve any relative subtree
569 /// specifications in the entity. If nothing is specified 649 /// specifications in the entity. If nothing is specified
570 /// then the whole inventory is implied. 650 /// then the whole of the private inventory is implied.
571 /// Please note that the subtree specified by the URI is only relevant 651 /// Please note that the subtree specified by the URI is only relevant
572 /// to an entity containing a URI relative specification, i.e. one or 652 /// to an entity containing a URI relative specification, i.e. one or
573 /// more elements do not specify parent folder information. These 653 /// more elements do not specify parent folder information. These
574 /// elements will be implicitly referenced within the context identified 654 /// elements will be implicitly referenced within the context identified
575 /// by the URI. 655 /// by the URI.
576 /// If an element in the entity specifies an explicit parent folder, then 656 /// If an element in the entity specifies an explicit parent folder, then
577 /// that parent is effective, regardless of any value specified in the 657 /// that parent is effective, regardless of any value specified in the
578 /// URI. If the parent does not exist, then the element, and any dependent 658 /// URI. If the parent does not exist, then the element, and any dependent
579 /// elements, are ignored. This case is actually detected and handled 659 /// elements, are ignored. This case is actually detected and handled
580 /// during the reconstitution process. 660 /// during the reconstitution process.
581 /// </summary> 661 /// </summary>
662 /// <param name=rdata>HTTP service request work area</param>
582 663
583 private void DoPost(InventoryRequestData rdata) 664 private void DoUpdate(InventoryRequestData rdata)
584 { 665 {
585 int count = 0; 666
667 int count = 0;
668 bool created = false;
669 bool modified = false;
586 670
587 // Resolve the inventory node that is to be modified. 671 // Resolve the inventory node that is to be modified.
672 // rdata already contains information about the current
673 // content of the user's inventory.
588 674
589 Object InventoryNode = getInventoryNode(rdata, rdata.root, 1); 675 Object InventoryNode = getInventoryNode(rdata, rdata.root, PARM_PATH, Rest.Fill);
590 676
591 // As long as we have a node, then we have something 677 // As long as we have a node, then we have something
592 // meaningful to do, unlike PUT. So we reconstitute the 678 // meaningful to do, unlike POST. So we reconstitute the
593 // subtree before doing anything else. Note that we 679 // subtree before doing anything else. Note that we
594 // etiher got a valid node or we threw an exception. 680 // etiher got a valid node or we threw an exception.
595 681
596 XmlInventoryCollection entity = ReconstituteEntity(rdata); 682 XmlInventoryCollection entity = ReconstituteEntity(rdata);
@@ -612,46 +698,77 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
612 698
613 Rest.AssetServices.AddAsset(asset); 699 Rest.AssetServices.AddAsset(asset);
614 700
615 if (Rest.DumpAsset) 701 created = true;
702 rdata.appendStatus(String.Format("<p> Created asset {0}, UUID {1}<p>", asset.Name, asset.ID));
703
704 if (Rest.DEBUG && Rest.DumpAsset)
616 { 705 {
617 Rest.Dump(asset.Data); 706 Rest.Dump(asset.Data);
618 } 707 }
708
619 } 709 }
620 } 710 }
621 711
622 /// <summary> 712 // The URI specifies either a folder or an item to be updated.
623 /// The URI specifies either a folder or an item to be updated. 713 //
624 /// </summary> 714 // The root node in the entity will replace the node identified
625 /// <remarks> 715 // by the URI. This means the parent will remain the same, but
626 /// The root node in the entity will replace the node identified 716 // any or all attributes associated with the named element
627 /// by the URI. This means the parent will remain the same, but 717 // will change.
628 /// any or all attributes associated with the named element 718 //
629 /// will change. 719 // If the inventory collection contains an element with a zero
630 /// 720 // parent ID, then this is taken to be the replacement for the
631 /// If the inventory collection contains an element with a zero 721 // named node. The collection MAY also specify an explicit
632 /// parent ID, then this is taken to be the replacement for the 722 // parent ID, in this case it MAY identify the same parent as
633 /// named node. The collection MAY also specify an explicit 723 // the current node, or it MAY specify a different parent,
634 /// parent ID, in this case it MAY identify the same parent as 724 // indicating that the folder is being moved in addition to any
635 /// the current node, or it MAY specify a different parent, 725 // other modifications being made.
636 /// indicating that the folder is being moved in addition to any
637 /// other modifications being made.
638 /// </remarks>
639 726
640 if (typeof(InventoryFolderBase) == InventoryNode.GetType() || 727 if (typeof(InventoryFolderBase) == InventoryNode.GetType() ||
641 typeof(InventoryFolderImpl) == InventoryNode.GetType()) 728 typeof(InventoryFolderImpl) == InventoryNode.GetType())
642 { 729 {
730
731 bool rfound = false;
643 InventoryFolderBase uri = (InventoryFolderBase) InventoryNode; 732 InventoryFolderBase uri = (InventoryFolderBase) InventoryNode;
644 InventoryFolderBase xml = null; 733 InventoryFolderBase xml = null;
734
735 // If the entity to be replaced resolved to be the root
736 // directory itself (My Inventory), then make sure that
737 // the supplied data include as appropriately typed and
738 // named folder. Note that we can;t rule out the possibility
739 // of a sub-directory being called "My Inventory", so that
740 // is anticipated.
741
742 if (uri == rdata.root)
743 {
744
745 foreach (InventoryFolderBase folder in entity.Folders)
746 {
747 if ((rfound = (folder.Name == PRIVATE_ROOT_NAME)))
748 {
749 if ((rfound = (folder.ParentID == LLUUID.Zero)))
750 break;
751 }
752 }
753
754 if (!rfound)
755 {
756 Rest.Log.DebugFormat("{0} {1}: Path <{2}> will result in loss of inventory",
757 MsgId, rdata.method, rdata.path);
758 rdata.Fail(Rest.HttpStatusCodeBadRequest, "invalid inventory structure");
759 }
760
761 }
645 762
646 // Scan the set of folders in the entity collection for an 763 // Scan the set of folders in the entity collection for an
647 // entry that matches the context folder. It is assumed that 764 // entry that matches the context folder. It is assumed that
648 // the only reliable indicator of this is a zero UUID (using 765 // the only reliable indicator of this is a zero UUID ( using
649 // implicit context), or the parent's UUID matches that of the 766 // implicit context), or the parent's UUID matches that of the
650 // URI designated node (explicit context). We don't allow 767 // URI designated node (explicit context). We don't allow
651 // ambiguity in this case because this is POST and we are 768 // ambiguity in this case because this is POST and we are
652 // supposed to be modifying a specific node. 769 // supposed to be modifying a specific node.
653 // We assign any element IDs required as an economy; we don't 770 // We assign any element IDs required as an economy; we don't
654 // want to iterate over the fodler set again if it can be 771 // want to iterate over the fodler set again if it can be
655 // helped. 772 // helped.
656 773
657 foreach (InventoryFolderBase folder in entity.Folders) 774 foreach (InventoryFolderBase folder in entity.Folders)
@@ -663,51 +780,60 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
663 xml = folder; 780 xml = folder;
664 count++; 781 count++;
665 } 782 }
666 if (xml.ID == LLUUID.Zero)
667 {
668 xml.ID = LLUUID.Random();
669 }
670 } 783 }
671 784
672 // More than one entry is ambiguous. Other folders should be 785 // More than one entry is ambiguous. Other folders should be
673 // added using the PUT verb. 786 // added using the POST verb.
674 787
675 if (count > 1) 788 if (count > 1)
676 { 789 {
677 Rest.Log.DebugFormat("{0} {1}: Request for <{2}> is ambiguous", 790 Rest.Log.DebugFormat("{0} {1}: Request for <{2}> is ambiguous",
678 MsgId, rdata.method, rdata.path); 791 MsgId, rdata.method, rdata.path);
679 rdata.Fail(Rest.HttpStatusCodeBadRequest, 792 rdata.Fail(Rest.HttpStatusCodeConflict, "context is ambiguous");
680 Rest.HttpStatusDescBadRequest+": context is ambiguous");
681 } 793 }
682 794
683 // Exactly one entry means we ARE replacing the node 795 // Exactly one entry means we ARE replacing the node
684 // identified by the URI. So we delete the old folder 796 // identified by the URI. So we delete the old folder
685 // by moving it to the trash and then purging it. 797 // by moving it to the trash and then purging it.
686 // We then add all of the folders and items we 798 // We then add all of the folders and items we
687 // included in the entity. The subtree has been 799 // included in the entity. The subtree has been
688 // modified. 800 // modified.
689 801
690 if (count == 1) 802 if (count == 1)
691 { 803 {
804
692 InventoryFolderBase TrashCan = GetTrashCan(rdata); 805 InventoryFolderBase TrashCan = GetTrashCan(rdata);
693 806
807 // All went well, so we generate a UUID is one is
808 // needed.
809
810 if (xml.ID == LLUUID.Zero)
811 {
812 xml.ID = LLUUID.Random();
813 }
814
694 uri.ParentID = TrashCan.ID; 815 uri.ParentID = TrashCan.ID;
695 Rest.InventoryServices.MoveFolder(uri); 816 Rest.InventoryServices.MoveFolder(uri);
696 Rest.InventoryServices.PurgeFolder(TrashCan); 817 Rest.InventoryServices.PurgeFolder(TrashCan);
818 modified = true;
819
697 } 820 }
698 821
699 // Now, regardelss of what they represent, we 822 // Now, regardelss of what they represent, we
700 // integrate all of the elements in the entity. 823 // integrate all of the elements in the entity.
701 824
702 foreach (InventoryFolderBase f in entity.Folders) 825 foreach (InventoryFolderBase f in entity.Folders)
703 { 826 {
827 rdata.appendStatus(String.Format("<p>Moving folder {0} UUID {1}<p>", f.Name, f.ID));
704 Rest.InventoryServices.MoveFolder(f); 828 Rest.InventoryServices.MoveFolder(f);
705 } 829 }
706 830
707 foreach (InventoryItemBase it in entity.Items) 831 foreach (InventoryItemBase it in entity.Items)
708 { 832 {
833 rdata.appendStatus(String.Format("<p>Storing item {0} UUID {1}<p>", it.Name, it.ID));
709 Rest.InventoryServices.AddItem(it); 834 Rest.InventoryServices.AddItem(it);
710 } 835 }
836
711 } 837 }
712 838
713 /// <summary> 839 /// <summary>
@@ -720,6 +846,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
720 846
721 else 847 else
722 { 848 {
849
723 InventoryItemBase uri = (InventoryItemBase) InventoryNode; 850 InventoryItemBase uri = (InventoryItemBase) InventoryNode;
724 InventoryItemBase xml = null; 851 InventoryItemBase xml = null;
725 852
@@ -727,20 +854,18 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
727 { 854 {
728 Rest.Log.DebugFormat("{0} {1}: Request should not contain any folders <{2}>", 855 Rest.Log.DebugFormat("{0} {1}: Request should not contain any folders <{2}>",
729 MsgId, rdata.method, rdata.path); 856 MsgId, rdata.method, rdata.path);
730 rdata.Fail(Rest.HttpStatusCodeBadRequest, 857 rdata.Fail(Rest.HttpStatusCodeBadRequest, "folder is not allowed");
731 Rest.HttpStatusDescBadRequest+": folder is not allowed");
732 } 858 }
733 859
734 if (entity.Items.Count > 1) 860 if (entity.Items.Count > 1)
735 { 861 {
736 Rest.Log.DebugFormat("{0} {1}: Entity contains too many items <{2}>", 862 Rest.Log.DebugFormat("{0} {1}: Entity contains too many items <{2}>",
737 MsgId, rdata.method, rdata.path); 863 MsgId, rdata.method, rdata.path);
738 rdata.Fail(Rest.HttpStatusCodeBadRequest, 864 rdata.Fail(Rest.HttpStatusCodeBadRequest, "too may items");
739 Rest.HttpStatusDescBadRequest+": too may items");
740 } 865 }
741 866
742 xml = entity.Items[0]; 867 xml = entity.Items[0];
743 868
744 if (xml.ID == LLUUID.Zero) 869 if (xml.ID == LLUUID.Zero)
745 { 870 {
746 xml.ID = LLUUID.Random(); 871 xml.ID = LLUUID.Random();
@@ -757,10 +882,29 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
757 // Add the new item to the inventory 882 // Add the new item to the inventory
758 883
759 Rest.InventoryServices.AddItem(xml); 884 Rest.InventoryServices.AddItem(xml);
885
886 rdata.appendStatus(String.Format("<p>Storing item {0} UUID {1}<p>", xml.Name, xml.ID));
887
888 }
889
890 if (created)
891 {
892 rdata.Complete(Rest.HttpStatusCodeCreated);
893 }
894 else
895 {
896 if (modified)
897 {
898 rdata.Complete(Rest.HttpStatusCodeOK);
899 }
900 else
901 {
902 rdata.Complete(Rest.HttpStatusCodeNoContent);
903 }
760 } 904 }
761 905
762 rdata.Complete();
763 rdata.Respond("Inventory " + rdata.method + ": Normal completion"); 906 rdata.Respond("Inventory " + rdata.method + ": Normal completion");
907
764 } 908 }
765 909
766 /// <summary> 910 /// <summary>
@@ -773,7 +917,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
773 /// 917 ///
774 /// Folders are deleted by moving them to another folder and then 918 /// Folders are deleted by moving them to another folder and then
775 /// purging that folder. We'll do that by creating a temporary 919 /// purging that folder. We'll do that by creating a temporary
776 /// sub-folder in the TrashCan and purging that folder's 920 /// sub-folder in the TrashCan and purging that folder's
777 /// contents. If we can't can it, we don't delete it... 921 /// contents. If we can't can it, we don't delete it...
778 /// So, if no trashcan is available, the request does nothing. 922 /// So, if no trashcan is available, the request does nothing.
779 /// Items are summarily deleted. 923 /// Items are summarily deleted.
@@ -782,14 +926,17 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
782 /// be performed using UUID, as a name might identify several 926 /// be performed using UUID, as a name might identify several
783 /// elements. 927 /// elements.
784 /// </summary> 928 /// </summary>
929 /// <param name=rdata>HTTP service request work area</param>
785 930
786 private void DoDelete(InventoryRequestData rdata) 931 private void DoDelete(InventoryRequestData rdata)
787 { 932 {
788 Object InventoryNode = getInventoryNode(rdata, rdata.root, 1); 933
934 Object InventoryNode = getInventoryNode(rdata, rdata.root, PARM_PATH, false);
789 935
790 if (typeof(InventoryFolderBase) == InventoryNode.GetType() || 936 if (typeof(InventoryFolderBase) == InventoryNode.GetType() ||
791 typeof(InventoryFolderImpl) == InventoryNode.GetType()) 937 typeof(InventoryFolderImpl) == InventoryNode.GetType())
792 { 938 {
939
793 InventoryFolderBase TrashCan = GetTrashCan(rdata); 940 InventoryFolderBase TrashCan = GetTrashCan(rdata);
794 941
795 InventoryFolderBase folder = (InventoryFolderBase) InventoryNode; 942 InventoryFolderBase folder = (InventoryFolderBase) InventoryNode;
@@ -798,6 +945,9 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
798 folder.ParentID = TrashCan.ID; 945 folder.ParentID = TrashCan.ID;
799 Rest.InventoryServices.MoveFolder(folder); 946 Rest.InventoryServices.MoveFolder(folder);
800 Rest.InventoryServices.PurgeFolder(TrashCan); 947 Rest.InventoryServices.PurgeFolder(TrashCan);
948
949 rdata.appendStatus(String.Format("<p>Deleted folder {0} UUID {1}<p>", folder.Name, folder.ID));
950
801 } 951 }
802 952
803 // Deleting items is much more straight forward. 953 // Deleting items is much more straight forward.
@@ -808,21 +958,23 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
808 Rest.Log.DebugFormat("{0} {1}: Item {2} will be deleted", 958 Rest.Log.DebugFormat("{0} {1}: Item {2} will be deleted",
809 MsgId, rdata.method, rdata.path); 959 MsgId, rdata.method, rdata.path);
810 Rest.InventoryServices.DeleteItem(item); 960 Rest.InventoryServices.DeleteItem(item);
961 rdata.appendStatus(String.Format("<p>Deleted item {0} UUID {1}<p>", item.Name, item.ID));
811 } 962 }
812 963
813 rdata.Complete(); 964 rdata.Complete();
814 rdata.Respond("Inventory " + rdata.method + ": Normal completion"); 965 rdata.Respond("Inventory " + rdata.method + ": Normal completion");
966
815 } 967 }
816 968
817 #endregion method-specific processing 969#endregion method-specific processing
818 970
819 /// <summary> 971 /// <summary>
820 /// This method is called to obtain the OpenSim inventory object identified 972 /// This method is called to obtain the OpenSim inventory object identified
821 /// by the supplied URI. This may be either an Item or a Folder, so a suitably 973 /// by the supplied URI. This may be either an Item or a Folder, so a suitably
822 /// ambiguous return type is employed (Object). This method recurses as 974 /// ambiguous return type is employed (Object). This method recurses as
823 /// necessary to process the designated hierarchy. 975 /// necessary to process the designated hierarchy.
824 /// 976 ///
825 /// If we reach the end of the URI then we return the contextural folder to 977 /// If we reach the end of the URI then we return the contextual folder to
826 /// our caller. 978 /// our caller.
827 /// 979 ///
828 /// If we are not yet at the end of the URI we attempt to find a child folder 980 /// If we are not yet at the end of the URI we attempt to find a child folder
@@ -831,50 +983,79 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
831 /// If this is the last node, then we look to see if this is an item. If it is, 983 /// If this is the last node, then we look to see if this is an item. If it is,
832 /// we return that item. 984 /// we return that item.
833 /// 985 ///
986 /// If we reach the end of an inventory path and the URI si not yet exhausted,
987 /// then if 'fill' is specified, we create the intermediate nodes.
988 ///
834 /// Otherwise we fail the request on the ground of an invalid URI. 989 /// Otherwise we fail the request on the ground of an invalid URI.
835 /// 990 ///
836 /// <note> 991 /// An ambiguous request causes the request to fail.
837 /// This mechanism cannot detect the case where duplicate subtrees satisfy a
838 /// request. In such a case the 1st element gets processed. If this is a
839 /// problem, then UUID should be used to identify the end-node. This is basic
840 /// premise of normal inventory processing. The name is an informational, and
841 /// not a defining, attribute.
842 /// </note>
843 /// 992 ///
844 /// </summary> 993 /// </summary>
845 994 /// <param name=rdata>HTTP service request work area</param>
846 private Object getInventoryNode(InventoryRequestData rdata, InventoryFolderBase folder, int pi) 995 /// <param name=folder>The folder to be searched (parent)</param>
996 /// <param name=pi>URI parameter index</param>
997 /// <param name=fill>Should missing path members be created?</param>
998
999 private Object getInventoryNode(InventoryRequestData rdata,
1000 InventoryFolderBase folder,
1001 int pi, bool fill)
847 { 1002 {
1003
1004 InventoryFolderBase foundf = null;
1005 int fk = 0;
1006
848 Rest.Log.DebugFormat("{0} Searching folder {1} {2} [{3}]", MsgId, folder.ID, folder.Name, pi); 1007 Rest.Log.DebugFormat("{0} Searching folder {1} {2} [{3}]", MsgId, folder.ID, folder.Name, pi);
849 1008
850 // We have just run off the end of the parameter sequence 1009 // We have just run off the end of the parameter sequence
851 1010
852 if (pi >= rdata.parameters.Length) 1011 if (pi >= rdata.Parameters.Length)
853 { 1012 {
854 return folder; 1013 return folder;
855 } 1014 }
856 1015
857 // More names in the sequence, look for a folder that might 1016 // There are more names in the parameter sequence,
858 // get us there. 1017 // look for the folder named by param[pi] as a
1018 // child of the folder supplied as an argument.
1019 // Note that a UUID may have been supplied as the
1020 // identifier (it is the ONLY guaranteed unambiguous
1021 // option.
859 1022
860 if (rdata.folders != null) 1023 if (rdata.folders != null)
861 {
862 foreach (InventoryFolderBase f in rdata.folders) 1024 foreach (InventoryFolderBase f in rdata.folders)
863 { 1025 {
864 // Look for the present node in the directory list 1026 // Look for the present node in the directory list
865 if (f.ParentID == folder.ID && 1027 if (f.ParentID == folder.ID &&
866 (f.Name == rdata.parameters[pi] || 1028 (f.Name == rdata.Parameters[pi] ||
867 f.ID.ToString() == rdata.parameters[pi])) 1029 f.ID.ToString() == rdata.Parameters[pi]))
868 { 1030 {
869 return getInventoryNode(rdata, f, pi+1); 1031 foundf = f;
1032 fk++;
870 } 1033 }
871 } 1034 }
1035
1036 // If more than one node matched, then the path, as specified
1037 // is ambiguous.
1038
1039 if (fk > 1)
1040 {
1041 Rest.Log.DebugFormat("{0} {1}: Request for {2} is ambiguous",
1042 MsgId, rdata.method, rdata.path);
1043 rdata.Fail(Rest.HttpStatusCodeConflict, "request is ambiguous");
1044 }
1045
1046 // If we find a match, then the method
1047 // increment the parameter index, and calls itself
1048 // passing the found folder as the new context.
1049
1050 if (foundf != null)
1051 {
1052 return getInventoryNode(rdata, foundf, pi+1, fill);
872 } 1053 }
873 1054
874 // No folders that match. Perhaps this parameter identifies an item? If 1055 // No folders that match. Perhaps this parameter identifies an item? If
875 // it does, then it MUST also be the last name in the sequence. 1056 // it does, then it MUST also be the last name in the sequence.
876 1057
877 if (pi == rdata.parameters.Length-1) 1058 if (pi == rdata.Parameters.Length-1)
878 { 1059 {
879 if (rdata.items != null) 1060 if (rdata.items != null)
880 { 1061 {
@@ -882,9 +1063,9 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
882 InventoryItemBase li = null; 1063 InventoryItemBase li = null;
883 foreach (InventoryItemBase i in rdata.items) 1064 foreach (InventoryItemBase i in rdata.items)
884 { 1065 {
885 if (i.Folder == folder.ID && 1066 if (i.Folder == folder.ID &&
886 (i.Name == rdata.parameters[pi] || 1067 (i.Name == rdata.Parameters[pi] ||
887 i.ID.ToString() == rdata.parameters[pi])) 1068 i.ID.ToString() == rdata.Parameters[pi]))
888 { 1069 {
889 li = i; 1070 li = i;
890 k++; 1071 k++;
@@ -894,26 +1075,35 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
894 { 1075 {
895 return li; 1076 return li;
896 } 1077 }
897 else 1078 else if (k > 1)
898 { 1079 {
899 Rest.Log.DebugFormat("{0} {1}: Request for {2} is ambiguous", 1080 Rest.Log.DebugFormat("{0} {1}: Request for {2} is ambiguous",
900 MsgId, rdata.method, rdata.path); 1081 MsgId, rdata.method, rdata.path);
901 rdata.Fail(Rest.HttpStatusCodeNotFound, Rest.HttpStatusDescNotFound+": request is ambiguous"); 1082 rdata.Fail(Rest.HttpStatusCodeConflict, "request is ambiguous");
902 } 1083 }
903 } 1084 }
904 } 1085 }
905 1086
906 // No, so abandon the request 1087 // If fill is enabled, then we must create the missing intermediate nodes.
1088 // And of course, even this is not straightforward. All intermediate nodes
1089 // are obviously folders, but the last node may be a folder or an item.
1090
1091 if (fill)
1092 {
1093 }
1094
1095 // No fill, so abandon the request
907 1096
908 Rest.Log.DebugFormat("{0} {1}: Resource {2} not found", 1097 Rest.Log.DebugFormat("{0} {1}: Resource {2} not found",
909 MsgId, rdata.method, rdata.path); 1098 MsgId, rdata.method, rdata.path);
910 rdata.Fail(Rest.HttpStatusCodeNotFound, Rest.HttpStatusDescNotFound+": resource "+rdata.path+" not found"); 1099 rdata.Fail(Rest.HttpStatusCodeNotFound, "resource "+rdata.path+" not found");
911 1100
912 return null; /* Never reached */ 1101 return null; /* Never reached */
1102
913 } 1103 }
914 1104
915 /// <summary> 1105 /// <summary>
916 /// This routine traverse the inventory's structure until the end-point identified 1106 /// This routine traverse the inventory's structure until the end-point identified
917 /// in the URI is reached, the remainder of the inventory (if any) is then formatted 1107 /// in the URI is reached, the remainder of the inventory (if any) is then formatted
918 /// and returned to the requestor. 1108 /// and returned to the requestor.
919 /// 1109 ///
@@ -923,57 +1113,142 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
923 /// 1113 ///
924 /// Only the last element in the URI should identify an item. 1114 /// Only the last element in the URI should identify an item.
925 /// </summary> 1115 /// </summary>
1116 /// <param name=rdata>HTTP service request work area</param>
1117 /// <param name=folder>The folder to be searched (parent)</param>
1118 /// <param name=pi>URI parameter index</param>
926 1119
927 private void traverseInventory(InventoryRequestData rdata, InventoryFolderBase folder, int pi) 1120 private void traverse(InventoryRequestData rdata, InventoryFolderBase folder, int pi)
928 { 1121 {
929 Rest.Log.DebugFormat("{0} Folder : {1} {2} [{3}]", MsgId, folder.ID, folder.Name, pi); 1122
1123 Rest.Log.DebugFormat("{0} Traverse[initial] : {1} {2} [{3}]", MsgId, folder.ID, folder.Name, pi);
930 1124
931 if (rdata.folders != null) 1125 if (rdata.folders != null)
932 { 1126 {
933 foreach (InventoryFolderBase f in rdata.folders) 1127
1128 // If there was only one parameter (avatar name), then the entire
1129 // inventory is being requested.
1130
1131 if (rdata.Parameters.Length == 1)
934 { 1132 {
935 if (f.ParentID == folder.ID && 1133 formatInventory(rdata, rdata.root, String.Empty);
936 (f.Name == rdata.parameters[pi] || 1134 }
937 f.ID.ToString() == rdata.parameters[pi])) 1135
1136 // Has the client specified the root directory name explicitly?
1137 // if yes, then we just absorb the reference, because the folder
1138 // we start looking in for a match *is* the root directory. If there
1139 // are more parameters remaining we tarverse, otehrwise it's time
1140 // to format. Otherwise,we consider the "My Inventory" to be implied
1141 // and we just traverse normally.
1142
1143 else if (folder.ID.ToString() == rdata.Parameters[pi] ||
1144 folder.Name == rdata.Parameters[pi])
1145 {
1146 // Length is -1 because the avatar name is a parameter
1147 if (pi<(rdata.Parameters.Length-1))
938 { 1148 {
939 if (pi < rdata.parameters.Length-1) 1149 traverseInventory(rdata, folder, pi+1);
940 {
941 traverseInventory(rdata, f, pi+1);
942 }
943 else
944 {
945 formatInventory(rdata, f, String.Empty);
946 }
947 return;
948 } 1150 }
1151 else
1152 {
1153 formatInventory(rdata, folder, String.Empty);
1154 }
1155 }
1156 else
1157 {
1158 traverseInventory(rdata, folder, pi);
949 } 1159 }
1160
1161 return;
1162
950 } 1163 }
1164 }
1165
1166 /// <summary>
1167 /// This is the recursive method. I've separated them in this way so that
1168 /// we do not have to waste cycles on any first-case-only processing.
1169 /// </summary>
1170
1171 private void traverseInventory(InventoryRequestData rdata, InventoryFolderBase folder, int pi)
1172 {
1173
1174 int fk = 0;
1175 InventoryFolderBase ffound = null;
1176 InventoryItemBase ifound = null;
951 1177
952 if (pi == rdata.parameters.Length-1) 1178 Rest.Log.DebugFormat("{0} Traverse Folder : {1} {2} [{3}]", MsgId, folder.ID, folder.Name, pi);
1179
1180 foreach (InventoryFolderBase f in rdata.folders)
953 { 1181 {
1182 if (f.ParentID == folder.ID &&
1183 (f.Name == rdata.Parameters[pi] ||
1184 f.ID.ToString() == rdata.Parameters[pi]))
1185 {
1186 fk++;
1187 ffound = f;
1188 }
1189 }
1190
1191 // If this is the last element in the parameter sequence, then
1192 // it is reasonable to check for an item. All intermediate nodes
1193 // MUST be folders.
1194
1195 if (pi == rdata.Parameters.Length-1)
1196 {
1197
1198 // Only if there are any items, and there pretty much always are.
1199
954 if (rdata.items != null) 1200 if (rdata.items != null)
955 { 1201 {
956 foreach (InventoryItemBase i in rdata.items) 1202 foreach (InventoryItemBase i in rdata.items)
957 { 1203 {
958 if (i.Folder == folder.ID && 1204 if (i.Folder == folder.ID &&
959 (i.Name == rdata.parameters[pi] || 1205 (i.Name == rdata.Parameters[pi] ||
960 i.ID.ToString() == rdata.parameters[pi])) 1206 i.ID.ToString() == rdata.Parameters[pi]))
961 { 1207 {
962 // Fetching an Item has a special significance. In this 1208 fk++;
963 // case we also want to fetch the associated asset. 1209 ifound = i;
964 // To make it interesting, we'll d this via redirection.
965 string asseturl = "http://" + rdata.hostname + ":" + rdata.port +
966 "/admin/assets" + Rest.UrlPathSeparator + i.AssetID.ToString();
967 rdata.Redirect(asseturl,Rest.PERMANENT);
968 Rest.Log.DebugFormat("{0} Never Reached");
969 } 1210 }
970 } 1211 }
971 } 1212 }
972 } 1213 }
973 1214
974 Rest.Log.DebugFormat("{0} Inventory does not contain item/folder: <{1}>", 1215 if (fk == 1)
1216 {
1217 if (ffound != null)
1218 {
1219 if (pi < rdata.Parameters.Length-1)
1220 {
1221 traverseInventory(rdata, ffound, pi+1);
1222 }
1223 else
1224 {
1225 formatInventory(rdata, ffound, String.Empty);
1226 }
1227 return;
1228 }
1229 else
1230 {
1231 // Fetching an Item has a special significance. In this
1232 // case we also want to fetch the associated asset.
1233 // To make it interesting, we'll d this via redirection.
1234 string asseturl = "http://" + rdata.hostname + ":" + rdata.port +
1235 "/admin/assets" + Rest.UrlPathSeparator + ifound.AssetID.ToString();
1236 rdata.Redirect(asseturl,Rest.PERMANENT);
1237 Rest.Log.DebugFormat("{0} Never Reached", MsgId);
1238 }
1239 }
1240 else if (fk > 1)
1241 {
1242 rdata.Fail(Rest.HttpStatusCodeConflict,
1243 String.Format("ambiguous element ({0}) in path specified: <{1}>",
1244 pi, rdata.path));
1245 }
1246
1247 Rest.Log.DebugFormat("{0} Inventory does not contain item/folder: <{1}>",
975 MsgId, rdata.path); 1248 MsgId, rdata.path);
976 rdata.Fail(Rest.HttpStatusCodeNotFound,Rest.HttpStatusDescNotFound+": no such item/folder"); 1249 rdata.Fail(Rest.HttpStatusCodeNotFound,String.Format("no such item/folder : {0}",
1250 rdata.Parameters[pi]));
1251
977 } 1252 }
978 1253
979 /// <summary> 1254 /// <summary>
@@ -983,12 +1258,17 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
983 /// The indentation parameter is solely for the benefit of trace record 1258 /// The indentation parameter is solely for the benefit of trace record
984 /// formatting. 1259 /// formatting.
985 /// </summary> 1260 /// </summary>
1261 /// <param name=rdata>HTTP service request work area</param>
1262 /// <param name=folder>The folder to be searched (parent)</param>
1263 /// <param name=indent>pretty print indentation</param>
986 1264
987 private void formatInventory(InventoryRequestData rdata, InventoryFolderBase folder, string indent) 1265 private void formatInventory(InventoryRequestData rdata, InventoryFolderBase folder, string indent)
988 { 1266 {
1267
989 if (Rest.DEBUG) 1268 if (Rest.DEBUG)
990 { 1269 {
991 Rest.Log.DebugFormat("{0} Folder : {1} {2} {3}", MsgId, folder.ID, indent, folder.Name); 1270 Rest.Log.DebugFormat("{0} Folder : {1} {2} {3} type = {4}",
1271 MsgId, folder.ID, indent, folder.Name, folder.Type);
992 indent += "\t"; 1272 indent += "\t";
993 } 1273 }
994 1274
@@ -997,6 +1277,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
997 rdata.writer.WriteStartElement(String.Empty,"Folder",String.Empty); 1277 rdata.writer.WriteStartElement(String.Empty,"Folder",String.Empty);
998 rdata.writer.WriteAttributeString("name",String.Empty,folder.Name); 1278 rdata.writer.WriteAttributeString("name",String.Empty,folder.Name);
999 rdata.writer.WriteAttributeString("uuid",String.Empty,folder.ID.ToString()); 1279 rdata.writer.WriteAttributeString("uuid",String.Empty,folder.ID.ToString());
1280 rdata.writer.WriteAttributeString("parent",String.Empty,folder.ParentID.ToString());
1000 rdata.writer.WriteAttributeString("owner",String.Empty,folder.Owner.ToString()); 1281 rdata.writer.WriteAttributeString("owner",String.Empty,folder.Owner.ToString());
1001 rdata.writer.WriteAttributeString("type",String.Empty,folder.Type.ToString()); 1282 rdata.writer.WriteAttributeString("type",String.Empty,folder.Type.ToString());
1002 rdata.writer.WriteAttributeString("version",String.Empty,folder.Version.ToString()); 1283 rdata.writer.WriteAttributeString("version",String.Empty,folder.Version.ToString());
@@ -1026,21 +1307,28 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
1026 // End folder item 1307 // End folder item
1027 1308
1028 rdata.writer.WriteEndElement(); 1309 rdata.writer.WriteEndElement();
1310
1029 } 1311 }
1030 1312
1031 /// <summary> 1313 /// <summary>
1032 /// This method generates XML that describes an instance of InventoryItemBase. 1314 /// This method generates XML that describes an instance of InventoryItemBase.
1033 /// </summary> 1315 /// </summary>
1316 /// <param name=rdata>HTTP service request work area</param>
1317 /// <param name=i>The item to be formatted</param>
1318 /// <param name=indent>Pretty print indentation</param>
1034 1319
1035 private void formatItem(InventoryRequestData rdata, InventoryItemBase i, string indent) 1320 private void formatItem(InventoryRequestData rdata, InventoryItemBase i, string indent)
1036 { 1321 {
1037 Rest.Log.DebugFormat("{0} Item : {1} {2} {3}", MsgId, i.ID, indent, i.Name); 1322
1323 Rest.Log.DebugFormat("{0} Item : {1} {2} {3} Type = {4}, AssetType = {5}",
1324 MsgId, i.ID, indent, i.Name, i.InvType, i.AssetType);
1038 1325
1039 rdata.writer.WriteStartElement(String.Empty,"Item",String.Empty); 1326 rdata.writer.WriteStartElement(String.Empty,"Item",String.Empty);
1040 1327
1041 rdata.writer.WriteAttributeString("name",String.Empty,i.Name); 1328 rdata.writer.WriteAttributeString("name",String.Empty,i.Name);
1042 rdata.writer.WriteAttributeString("desc",String.Empty,i.Description); 1329 rdata.writer.WriteAttributeString("desc",String.Empty,i.Description);
1043 rdata.writer.WriteAttributeString("uuid",String.Empty,i.ID.ToString()); 1330 rdata.writer.WriteAttributeString("uuid",String.Empty,i.ID.ToString());
1331 rdata.writer.WriteAttributeString("folder",String.Empty,i.Folder.ToString());
1044 rdata.writer.WriteAttributeString("owner",String.Empty,i.Owner.ToString()); 1332 rdata.writer.WriteAttributeString("owner",String.Empty,i.Owner.ToString());
1045 rdata.writer.WriteAttributeString("creator",String.Empty,i.Creator.ToString()); 1333 rdata.writer.WriteAttributeString("creator",String.Empty,i.Creator.ToString());
1046 rdata.writer.WriteAttributeString("creationdate",String.Empty,i.CreationDate.ToString()); 1334 rdata.writer.WriteAttributeString("creationdate",String.Empty,i.CreationDate.ToString());
@@ -1062,10 +1350,11 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
1062 rdata.writer.WriteElementString("Asset",i.AssetID.ToString()); 1350 rdata.writer.WriteElementString("Asset",i.AssetID.ToString());
1063 1351
1064 rdata.writer.WriteEndElement(); 1352 rdata.writer.WriteEndElement();
1353
1065 } 1354 }
1066 1355
1067 /// <summary> 1356 /// <summary>
1068 /// This method creates a "trashcan" folder to support folder and item 1357 /// This method creates a "trashcan" folder to support folder and item
1069 /// deletions by this interface. The xisting trash folder is found and 1358 /// deletions by this interface. The xisting trash folder is found and
1070 /// this folder is created within it. It is called "tmp" to indicate to 1359 /// this folder is created within it. It is called "tmp" to indicate to
1071 /// the client that it is OK to delete this folder. The REST interface 1360 /// the client that it is OK to delete this folder. The REST interface
@@ -1073,9 +1362,11 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
1073 /// If the trash can cannot be created, then by implication the request 1362 /// If the trash can cannot be created, then by implication the request
1074 /// that required it cannot be completed, and it fails accordingly. 1363 /// that required it cannot be completed, and it fails accordingly.
1075 /// </summary> 1364 /// </summary>
1365 /// <param name=rdata>HTTP service request work area</param>
1076 1366
1077 private InventoryFolderBase GetTrashCan(InventoryRequestData rdata) 1367 private InventoryFolderBase GetTrashCan(InventoryRequestData rdata)
1078 { 1368 {
1369
1079 InventoryFolderBase TrashCan = null; 1370 InventoryFolderBase TrashCan = null;
1080 1371
1081 foreach (InventoryFolderBase f in rdata.folders) 1372 foreach (InventoryFolderBase f in rdata.folders)
@@ -1102,29 +1393,31 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
1102 } 1393 }
1103 } 1394 }
1104 } 1395 }
1105 1396
1106 if (TrashCan == null) 1397 if (TrashCan == null)
1107 { 1398 {
1108 Rest.Log.DebugFormat("{0} No Trash Can available", MsgId); 1399 Rest.Log.DebugFormat("{0} No Trash Can available", MsgId);
1109 rdata.Fail(Rest.HttpStatusCodeServerError, 1400 rdata.Fail(Rest.HttpStatusCodeServerError, "unable to create trash can");
1110 Rest.HttpStatusDescServerError+": unable to create trash can");
1111 } 1401 }
1112 1402
1113 return TrashCan; 1403 return TrashCan;
1404
1114 } 1405 }
1115 1406
1116 /// <summary> 1407 /// <summary>
1117 /// Make sure that an unchanged folder is not unnecessarily 1408 /// Make sure that an unchanged folder is not unnecessarily
1118 /// processed. 1409 /// processed.
1119 /// </summary> 1410 /// </summary>
1411 /// <param name=newf>Folder obtained from enclosed entity</param>
1412 /// <param name=oldf>Folder obtained from the user's inventory</param>
1120 1413
1121 private bool FolderHasChanged(InventoryFolderBase newf, InventoryFolderBase oldf) 1414 private bool FolderHasChanged(InventoryFolderBase newf, InventoryFolderBase oldf)
1122 { 1415 {
1123 return (newf.Name != oldf.Name 1416 return ( newf.Name != oldf.Name
1124 || newf.ParentID != oldf.ParentID 1417 || newf.ParentID != oldf.ParentID
1125 || newf.Owner != oldf.Owner 1418 || newf.Owner != oldf.Owner
1126 || newf.Type != oldf.Type 1419 || newf.Type != oldf.Type
1127 || newf.Version != oldf.Version 1420 || newf.Version != oldf.Version
1128 ); 1421 );
1129 } 1422 }
1130 1423
@@ -1132,27 +1425,29 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
1132 /// Make sure that an unchanged item is not unnecessarily 1425 /// Make sure that an unchanged item is not unnecessarily
1133 /// processed. 1426 /// processed.
1134 /// </summary> 1427 /// </summary>
1428 /// <param name=newf>Item obtained from enclosed entity</param>
1429 /// <param name=oldf>Item obtained from the user's inventory</param>
1135 1430
1136 private bool ItemHasChanged(InventoryItemBase newf, InventoryItemBase oldf) 1431 private bool ItemHasChanged(InventoryItemBase newf, InventoryItemBase oldf)
1137 { 1432 {
1138 return (newf.Name != oldf.Name 1433 return ( newf.Name != oldf.Name
1139 || newf.Folder != oldf.Description 1434 || newf.Folder != oldf.Description
1140 || newf.Description != oldf.Description 1435 || newf.Description != oldf.Description
1141 || newf.Owner != oldf.Owner 1436 || newf.Owner != oldf.Owner
1142 || newf.Creator != oldf.Creator 1437 || newf.Creator != oldf.Creator
1143 || newf.AssetID != oldf.AssetID 1438 || newf.AssetID != oldf.AssetID
1144 || newf.GroupID != oldf.GroupID 1439 || newf.GroupID != oldf.GroupID
1145 || newf.GroupOwned != oldf.GroupOwned 1440 || newf.GroupOwned != oldf.GroupOwned
1146 || newf.InvType != oldf.InvType 1441 || newf.InvType != oldf.InvType
1147 || newf.AssetType != oldf.AssetType 1442 || newf.AssetType != oldf.AssetType
1148 ); 1443 );
1149 } 1444 }
1150 1445
1151 /// <summary> 1446 /// <summary>
1152 /// This method is called by PUT and POST to create an XmlInventoryCollection 1447 /// This method is called by PUT and POST to create an XmlInventoryCollection
1153 /// instance that reflects the content of the entity supplied on the request. 1448 /// instance that reflects the content of the entity supplied on the request.
1154 /// Any elements in the completed collection whose UUID is zero, are 1449 /// Any elements in the completed collection whose UUID is zero, are
1155 /// considered to be located relative to the end-point identified int he 1450 /// considered to be located relative to the end-point identified int he
1156 /// URI. In this way, an entire sub-tree can be conveyed in a single REST 1451 /// URI. In this way, an entire sub-tree can be conveyed in a single REST
1157 /// PUT or POST request. 1452 /// PUT or POST request.
1158 /// 1453 ///
@@ -1160,24 +1455,27 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
1160 /// has an entity, it is more completely initialized. thus, if no entity was 1455 /// has an entity, it is more completely initialized. thus, if no entity was
1161 /// provided the collection is valid, but empty. 1456 /// provided the collection is valid, but empty.
1162 /// 1457 ///
1163 /// The entity is then scanned and each tag is processed to produce the 1458 /// The entity is then scanned and each tag is processed to produce the
1164 /// appropriate inventory elements. At the end f the scan, teh XmlInventoryCollection 1459 /// appropriate inventory elements. At the end f the scan, teh XmlInventoryCollection
1165 /// will reflect the subtree described by the entity. 1460 /// will reflect the subtree described by the entity.
1166 /// 1461 ///
1167 /// This is a very flexible mechanism, the entity may contain arbitrary, 1462 /// This is a very flexible mechanism, the entity may contain arbitrary,
1168 /// discontiguous tree fragments, or may contain single element. The caller is 1463 /// discontiguous tree fragments, or may contain single element. The caller is
1169 /// responsible for integrating this collection (and ensuring that any 1464 /// responsible for integrating this collection (and ensuring that any
1170 /// missing parent IDs are resolved). 1465 /// missing parent IDs are resolved).
1171 /// </summary> 1466 /// </summary>
1467 /// <param name=rdata>HTTP service request work area</param>
1172 1468
1173 internal XmlInventoryCollection ReconstituteEntity(InventoryRequestData rdata) 1469 internal XmlInventoryCollection ReconstituteEntity(InventoryRequestData rdata)
1174 { 1470 {
1471
1175 Rest.Log.DebugFormat("{0} Reconstituting entity", MsgId); 1472 Rest.Log.DebugFormat("{0} Reconstituting entity", MsgId);
1176 1473
1177 XmlInventoryCollection ic = new XmlInventoryCollection(); 1474 XmlInventoryCollection ic = new XmlInventoryCollection();
1178 1475
1179 if (rdata.request.HasEntityBody) 1476 if (rdata.request.HasEntityBody)
1180 { 1477 {
1478
1181 Rest.Log.DebugFormat("{0} Entity present", MsgId); 1479 Rest.Log.DebugFormat("{0} Entity present", MsgId);
1182 1480
1183 ic.init(rdata); 1481 ic.init(rdata);
@@ -1273,6 +1571,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
1273 Rest.Log.WarnFormat("{0} Unexpected XML parsing error: {1}", MsgId, e.Message); 1571 Rest.Log.WarnFormat("{0} Unexpected XML parsing error: {1}", MsgId, e.Message);
1274 throw e; 1572 throw e;
1275 } 1573 }
1574
1276 } 1575 }
1277 else 1576 else
1278 { 1577 {
@@ -1288,13 +1587,14 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
1288 } 1587 }
1289 1588
1290 return ic; 1589 return ic;
1590
1291 } 1591 }
1292 1592
1293 /// <summary> 1593 /// <summary>
1294 /// This method creates an inventory Folder from the 1594 /// This method creates an inventory Folder from the
1295 /// information supplied in the request's entity. 1595 /// information supplied in the request's entity.
1296 /// A folder instance is created and initialized to reflect 1596 /// A folder instance is created and initialized to reflect
1297 /// default values. These values are then overridden 1597 /// default values. These values are then overridden
1298 /// by information supplied in the entity. 1598 /// by information supplied in the entity.
1299 /// If context was not explicitly provided, then the 1599 /// If context was not explicitly provided, then the
1300 /// appropriate ID values are determined. 1600 /// appropriate ID values are determined.
@@ -1302,6 +1602,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
1302 1602
1303 private void CollectFolder(XmlInventoryCollection ic) 1603 private void CollectFolder(XmlInventoryCollection ic)
1304 { 1604 {
1605
1305 Rest.Log.DebugFormat("{0} Interpret folder element", MsgId); 1606 Rest.Log.DebugFormat("{0} Interpret folder element", MsgId);
1306 1607
1307 InventoryFolderBase result = new InventoryFolderBase(); 1608 InventoryFolderBase result = new InventoryFolderBase();
@@ -1341,10 +1642,10 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
1341 result.Version = UInt16.Parse(ic.xml.Value); 1642 result.Version = UInt16.Parse(ic.xml.Value);
1342 break; 1643 break;
1343 default : 1644 default :
1344 Rest.Log.DebugFormat("{0} Folder: unrecognized attribute: {1}:{2}", 1645 Rest.Log.DebugFormat("{0} Folder: unrecognized attribute: {1}:{2}",
1345 MsgId, ic.xml.Name, ic.xml.Value); 1646 MsgId, ic.xml.Name, ic.xml.Value);
1346 ic.Fail(Rest.HttpStatusCodeBadRequest, 1647 ic.Fail(Rest.HttpStatusCodeBadRequest, String.Format("unrecognized attribute <{0}>",
1347 Rest.HttpStatusDescBadRequest+": unrecognized attribute"); 1648 ic.xml.Name));
1348 break; 1649 break;
1349 } 1650 }
1350 } 1651 }
@@ -1363,11 +1664,12 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
1363 } 1664 }
1364 else 1665 else
1365 { 1666 {
1667
1366 bool found = false; 1668 bool found = false;
1367 1669
1368 foreach (InventoryFolderBase parent in ic.rdata.folders) 1670 foreach (InventoryFolderBase parent in ic.rdata.folders)
1369 { 1671 {
1370 if (parent.ID == result.ParentID) 1672 if ( parent.ID == result.ParentID )
1371 { 1673 {
1372 found = true; 1674 found = true;
1373 break; 1675 break;
@@ -1376,11 +1678,11 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
1376 1678
1377 if (!found) 1679 if (!found)
1378 { 1680 {
1379 Rest.Log.ErrorFormat("{0} Invalid parent ID ({1}) in folder {2}", 1681 Rest.Log.ErrorFormat("{0} Invalid parent ID ({1}) in folder {2}",
1380 MsgId, ic.Item.Folder, result.ID); 1682 MsgId, ic.Item.Folder, result.ID);
1381 ic.Fail(Rest.HttpStatusCodeBadRequest, 1683 ic.Fail(Rest.HttpStatusCodeBadRequest, "invalid parent");
1382 Rest.HttpStatusDescBadRequest+": invalid parent");
1383 } 1684 }
1685
1384 } 1686 }
1385 1687
1386 // This is a new folder, so no existing UUID is available 1688 // This is a new folder, so no existing UUID is available
@@ -1395,14 +1697,15 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
1395 // obsolete as a consequence. 1697 // obsolete as a consequence.
1396 1698
1397 ic.Push(result); 1699 ic.Push(result);
1700
1398 } 1701 }
1399 1702
1400 /// <summary> 1703 /// <summary>
1401 /// This method is called to handle the construction of an Item 1704 /// This method is called to handle the construction of an Item
1402 /// instance from the supplied request entity. It is called 1705 /// instance from the supplied request entity. It is called
1403 /// whenever an Item start tag is detected. 1706 /// whenever an Item start tag is detected.
1404 /// An instance of an Item is created and initialized to default 1707 /// An instance of an Item is created and initialized to default
1405 /// values. These values are then overridden from values supplied 1708 /// values. These values are then overridden from values supplied
1406 /// as attributes to the Item element. 1709 /// as attributes to the Item element.
1407 /// This item is then stored in the XmlInventoryCollection and 1710 /// This item is then stored in the XmlInventoryCollection and
1408 /// will be verified by Validate. 1711 /// will be verified by Validate.
@@ -1412,6 +1715,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
1412 1715
1413 private void CollectItem(XmlInventoryCollection ic) 1716 private void CollectItem(XmlInventoryCollection ic)
1414 { 1717 {
1718
1415 Rest.Log.DebugFormat("{0} Interpret item element", MsgId); 1719 Rest.Log.DebugFormat("{0} Interpret item element", MsgId);
1416 1720
1417 InventoryItemBase result = new InventoryItemBase(); 1721 InventoryItemBase result = new InventoryItemBase();
@@ -1432,6 +1736,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
1432 { 1736 {
1433 for (int i = 0; i < ic.xml.AttributeCount; i++) 1737 for (int i = 0; i < ic.xml.AttributeCount; i++)
1434 { 1738 {
1739
1435 ic.xml.MoveToAttribute(i); 1740 ic.xml.MoveToAttribute(i);
1436 1741
1437 switch (ic.xml.Name) 1742 switch (ic.xml.Name)
@@ -1480,36 +1785,37 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
1480 break; 1785 break;
1481 1786
1482 default : 1787 default :
1483 Rest.Log.DebugFormat("{0} Item: Unrecognized attribute: {1}:{2}", 1788 Rest.Log.DebugFormat("{0} Item: Unrecognized attribute: {1}:{2}",
1484 MsgId, ic.xml.Name, ic.xml.Value); 1789 MsgId, ic.xml.Name, ic.xml.Value);
1485 ic.Fail(Rest.HttpStatusCodeBadRequest, 1790 ic.Fail(Rest.HttpStatusCodeBadRequest, String.Format("unrecognized attribute",
1486 Rest.HttpStatusDescBadRequest+": unrecognized attribute"); 1791 ic.xml.Name));
1487 break; 1792 break;
1488 } 1793 }
1489 } 1794 }
1490 } 1795 }
1491 1796
1492 ic.xml.MoveToElement(); 1797 ic.xml.MoveToElement();
1493 1798
1494 ic.Push(result); 1799 ic.Push(result);
1800
1495 } 1801 }
1496 1802
1497 /// <summary> 1803 /// <summary>
1498 /// This method assembles an asset instance from the 1804 /// This method assembles an asset instance from the
1499 /// information supplied in the request's entity. It is 1805 /// information supplied in the request's entity. It is
1500 /// called as a result of detecting a start tag for a 1806 /// called as a result of detecting a start tag for a
1501 /// type of Asset. 1807 /// type of Asset.
1502 /// The information is collected locally, and an asset 1808 /// The information is collected locally, and an asset
1503 /// instance is created only if the basic XML parsing 1809 /// instance is created only if the basic XML parsing
1504 /// completes successfully. 1810 /// completes successfully.
1505 /// Default values for all parts of the asset are 1811 /// Default values for all parts of the asset are
1506 /// established before overriding them from the supplied 1812 /// established before overriding them from the supplied
1507 /// XML. 1813 /// XML.
1508 /// If an asset has inline=true as an attribute, then 1814 /// If an asset has inline=true as an attribute, then
1509 /// the element contains the data representing the 1815 /// the element contains the data representing the
1510 /// asset. This is saved as the data component. 1816 /// asset. This is saved as the data component.
1511 /// inline=false means that the element's payload is 1817 /// inline=false means that the element's payload is
1512 /// simply the UUID of the asset referenced by the 1818 /// simply the UUID of the asset referenced by the
1513 /// item being constructed. 1819 /// item being constructed.
1514 /// An asset, if created is stored in the 1820 /// An asset, if created is stored in the
1515 /// XmlInventoryCollection 1821 /// XmlInventoryCollection
@@ -1570,10 +1876,10 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
1570 break; 1876 break;
1571 1877
1572 default : 1878 default :
1573 Rest.Log.DebugFormat("{0} Asset: Unrecognized attribute: {1}:{2}", 1879 Rest.Log.DebugFormat("{0} Asset: Unrecognized attribute: {1}:{2}",
1574 MsgId, ic.xml.Name, ic.xml.Value); 1880 MsgId, ic.xml.Name, ic.xml.Value);
1575 ic.Fail(Rest.HttpStatusCodeBadRequest, 1881 ic.Fail(Rest.HttpStatusCodeBadRequest,
1576 Rest.HttpStatusDescBadRequest); 1882 String.Format("unrecognized attribute <{0}>", ic.xml.Name));
1577 break; 1883 break;
1578 } 1884 }
1579 } 1885 }
@@ -1583,7 +1889,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
1583 1889
1584 // If this is a reference to an existing asset, just store the 1890 // If this is a reference to an existing asset, just store the
1585 // asset ID into the item. 1891 // asset ID into the item.
1586 1892
1587 if (!inline) 1893 if (!inline)
1588 { 1894 {
1589 if (ic.Item != null) 1895 if (ic.Item != null)
@@ -1594,17 +1900,17 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
1594 else 1900 else
1595 { 1901 {
1596 Rest.Log.DebugFormat("{0} LLUID unimbedded asset must be inline", MsgId); 1902 Rest.Log.DebugFormat("{0} LLUID unimbedded asset must be inline", MsgId);
1597 ic.Fail(Rest.HttpStatusCodeBadRequest, 1903 ic.Fail(Rest.HttpStatusCodeBadRequest, "no context for asset");
1598 Rest.HttpStatusDescBadRequest+": no context for asset");
1599 } 1904 }
1600 } 1905 }
1601 1906
1602 // Otherwise, generate an asset ID, store that into the item, and 1907 // Otherwise, generate an asset ID, store that into the item, and
1603 // create an entry in the asset list for the inlined asset. But 1908 // create an entry in the asset list for the inlined asset. But
1604 // only if the size is non-zero. 1909 // only if the size is non-zero.
1605 1910
1606 else 1911 else
1607 { 1912 {
1913
1608 string b64string = null; 1914 string b64string = null;
1609 1915
1610 // Generate a UUID of none were given, and generally none should 1916 // Generate a UUID of none were given, and generally none should
@@ -1617,17 +1923,17 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
1617 1923
1618 // Create AssetBase entity to hold the inlined asset 1924 // Create AssetBase entity to hold the inlined asset
1619 1925
1620 asset = new AssetBase(uuid, name); 1926 asset = new AssetBase(uuid, name);
1621 1927
1622 asset.Description = desc; 1928 asset.Description = desc;
1623 asset.Type = type; // type == 0 == texture 1929 asset.Type = type; // type == 0 == texture
1624 asset.Local = local; 1930 asset.Local = local;
1625 asset.Temporary = temp; 1931 asset.Temporary = temp;
1626 1932
1627 b64string = ic.xml.ReadElementContentAsString(); 1933 b64string = ic.xml.ReadElementContentAsString();
1628 1934
1629 Rest.Log.DebugFormat("{0} Data length is {1}", MsgId, b64string.Length); 1935 Rest.Log.DebugFormat("{0} Data length is {1}", MsgId, b64string.Length);
1630 Rest.Log.DebugFormat("{0} Data content starts with: \n\t<{1}>", MsgId, 1936 Rest.Log.DebugFormat("{0} Data content starts with: \n\t<{1}>", MsgId,
1631 b64string.Substring(0, b64string.Length > 132 ? 132 : b64string.Length)); 1937 b64string.Substring(0, b64string.Length > 132 ? 132 : b64string.Length));
1632 1938
1633 asset.Data = Convert.FromBase64String(b64string); 1939 asset.Data = Convert.FromBase64String(b64string);
@@ -1646,19 +1952,22 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
1646 { 1952 {
1647 ic.Item.AssetID = uuid; 1953 ic.Item.AssetID = uuid;
1648 } 1954 }
1955
1649 } 1956 }
1650 1957
1651 ic.Push(asset); 1958 ic.Push(asset);
1959
1652 } 1960 }
1653 1961
1654 /// <summary> 1962 /// <summary>
1655 /// Store any permissions information provided by the request. 1963 /// Store any permissions information provided by the request.
1656 /// This overrides the default permissions set when the 1964 /// This overrides the default permissions set when the
1657 /// XmlInventoryCollection object was created. 1965 /// XmlInventoryCollection object was created.
1658 /// </summary> 1966 /// </summary>
1659 1967
1660 private void CollectPermissions(XmlInventoryCollection ic) 1968 private void CollectPermissions(XmlInventoryCollection ic)
1661 { 1969 {
1970
1662 if (ic.xml.HasAttributes) 1971 if (ic.xml.HasAttributes)
1663 { 1972 {
1664 for (int i = 0; i < ic.xml.AttributeCount; i++) 1973 for (int i = 0; i < ic.xml.AttributeCount; i++)
@@ -1681,14 +1990,15 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
1681 default : 1990 default :
1682 Rest.Log.DebugFormat("{0} Permissions: invalid attribute {1}:{2}", 1991 Rest.Log.DebugFormat("{0} Permissions: invalid attribute {1}:{2}",
1683 MsgId,ic.xml.Name, ic.xml.Value); 1992 MsgId,ic.xml.Name, ic.xml.Value);
1684 ic.Fail(Rest.HttpStatusCodeBadRequest, 1993 ic.Fail(Rest.HttpStatusCodeBadRequest,
1685 Rest.HttpStatusDescBadRequest); 1994 String.Format("invalid attribute <{0}>", ic.xml.Name));
1686 break; 1995 break;
1687 } 1996 }
1688 } 1997 }
1689 } 1998 }
1690 1999
1691 ic.xml.MoveToElement(); 2000 ic.xml.MoveToElement();
2001
1692 } 2002 }
1693 2003
1694 /// <summary> 2004 /// <summary>
@@ -1703,35 +2013,35 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
1703 2013
1704 private void Validate(XmlInventoryCollection ic) 2014 private void Validate(XmlInventoryCollection ic)
1705 { 2015 {
2016
1706 // There really should be an item present if we've 2017 // There really should be an item present if we've
1707 // called validate. So fail if there is not. 2018 // called validate. So fail if there is not.
1708 2019
1709 if (ic.Item == null) 2020 if (ic.Item == null)
1710 { 2021 {
1711 Rest.Log.ErrorFormat("{0} Unable to parse request", MsgId); 2022 Rest.Log.ErrorFormat("{0} Unable to parse request", MsgId);
1712 ic.Fail(Rest.HttpStatusCodeBadRequest, 2023 ic.Fail(Rest.HttpStatusCodeBadRequest, "request parse error");
1713 Rest.HttpStatusDescBadRequest+": request parse error");
1714 } 2024 }
1715 2025
1716 // Every item is required to have a name (via REST anyway) 2026 // Every item is required to have a name (via REST anyway)
1717 2027
1718 if (ic.Item.Name == String.Empty) 2028 if (ic.Item.Name == String.Empty)
1719 { 2029 {
1720 Rest.Log.ErrorFormat("{0} An item name MUST be specified", MsgId); 2030 Rest.Log.ErrorFormat("{0} An item name MUST be specified", MsgId);
1721 ic.Fail(Rest.HttpStatusCodeBadRequest, 2031 ic.Fail(Rest.HttpStatusCodeBadRequest, "item name required");
1722 Rest.HttpStatusDescBadRequest+": item name required");
1723 } 2032 }
1724 2033
1725 // An item MUST have an asset ID. AssetID should never be zero 2034 // An item MUST have an asset ID. AssetID should never be zero
1726 // here. It should always get set from the information stored 2035 // here. It should always get set from the information stored
1727 // when the Asset element was processed. 2036 // when the Asset element was processed.
1728 2037
1729 if (ic.Item.AssetID == LLUUID.Zero) 2038 if (ic.Item.AssetID == LLUUID.Zero)
1730 { 2039 {
2040
1731 Rest.Log.ErrorFormat("{0} Unable to complete request", MsgId); 2041 Rest.Log.ErrorFormat("{0} Unable to complete request", MsgId);
1732 Rest.Log.InfoFormat("{0} Asset information is missing", MsgId); 2042 Rest.Log.InfoFormat("{0} Asset information is missing", MsgId);
1733 ic.Fail(Rest.HttpStatusCodeBadRequest, 2043 ic.Fail(Rest.HttpStatusCodeBadRequest, "asset information required");
1734 Rest.HttpStatusDescBadRequest+": asset information required"); 2044
1735 } 2045 }
1736 2046
1737 // If the item is new, then assign it an ID 2047 // If the item is new, then assign it an ID
@@ -1744,18 +2054,19 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
1744 // If the context is being implied, obtain the current 2054 // If the context is being implied, obtain the current
1745 // folder item's ID. If it was specified explicitly, make 2055 // folder item's ID. If it was specified explicitly, make
1746 // sure that theparent folder exists. 2056 // sure that theparent folder exists.
1747 2057
1748 if (ic.Item.Folder == LLUUID.Zero) 2058 if (ic.Item.Folder == LLUUID.Zero)
1749 { 2059 {
1750 ic.Item.Folder = ic.Parent(); 2060 ic.Item.Folder = ic.Parent();
1751 } 2061 }
1752 else 2062 else
1753 { 2063 {
2064
1754 bool found = false; 2065 bool found = false;
1755 2066
1756 foreach (InventoryFolderBase parent in ic.rdata.folders) 2067 foreach (InventoryFolderBase parent in ic.rdata.folders)
1757 { 2068 {
1758 if (parent.ID == ic.Item.Folder) 2069 if ( parent.ID == ic.Item.Folder )
1759 { 2070 {
1760 found = true; 2071 found = true;
1761 break; 2072 break;
@@ -1764,11 +2075,11 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
1764 2075
1765 if (!found) 2076 if (!found)
1766 { 2077 {
1767 Rest.Log.ErrorFormat("{0} Invalid parent ID ({1}) in item {2}", 2078 Rest.Log.ErrorFormat("{0} Invalid parent ID ({1}) in item {2}",
1768 MsgId, ic.Item.Folder, ic.Item.ID); 2079 MsgId, ic.Item.Folder, ic.Item.ID);
1769 ic.Fail(Rest.HttpStatusCodeBadRequest, 2080 ic.Fail(Rest.HttpStatusCodeBadRequest, "parent information required");
1770 Rest.HttpStatusDescBadRequest+": parent information required");
1771 } 2081 }
2082
1772 } 2083 }
1773 2084
1774 // If this is an inline asset being constructed in the context 2085 // If this is an inline asset being constructed in the context
@@ -1790,12 +2101,13 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
1790 ic.Item.NextPermissions = ic.NextPermissions; 2101 ic.Item.NextPermissions = ic.NextPermissions;
1791 2102
1792 // If no type was specified for this item, we can attempt to 2103 // If no type was specified for this item, we can attempt to
1793 // infer something from the file type maybe. This is NOT as 2104 // infer something from the file type maybe. This is NOT as
1794 // good as having type be specified in the XML. 2105 // good as having type be specified in the XML.
1795 2106
1796 if (ic.Item.AssetType == (int) AssetType.Unknown || 2107 if (ic.Item.AssetType == (int) AssetType.Unknown ||
1797 ic.Item.InvType == (int) AssetType.Unknown) 2108 ic.Item.InvType == (int) AssetType.Unknown)
1798 { 2109 {
2110
1799 Rest.Log.DebugFormat("{0} Attempting to infer item type", MsgId); 2111 Rest.Log.DebugFormat("{0} Attempting to infer item type", MsgId);
1800 2112
1801 string[] parts = ic.Item.Name.Split(Rest.CA_PERIOD); 2113 string[] parts = ic.Item.Name.Split(Rest.CA_PERIOD);
@@ -1815,7 +2127,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
1815 2127
1816 if (parts.Length > 1) 2128 if (parts.Length > 1)
1817 { 2129 {
1818 Rest.Log.DebugFormat("{0} File type is {1}", 2130 Rest.Log.DebugFormat("{0} File type is {1}",
1819 MsgId, parts[parts.Length - 1]); 2131 MsgId, parts[parts.Length - 1]);
1820 switch (parts[parts.Length - 1]) 2132 switch (parts[parts.Length - 1])
1821 { 2133 {
@@ -1823,7 +2135,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
1823 case "jpeg-2000" : 2135 case "jpeg-2000" :
1824 case "jpg2000" : 2136 case "jpg2000" :
1825 case "jpg-2000" : 2137 case "jpg-2000" :
1826 Rest.Log.DebugFormat("{0} Type {1} inferred", 2138 Rest.Log.DebugFormat("{0} Type {1} inferred",
1827 MsgId, parts[parts.Length-1]); 2139 MsgId, parts[parts.Length-1]);
1828 if (ic.Item.AssetType == (int) AssetType.Unknown) 2140 if (ic.Item.AssetType == (int) AssetType.Unknown)
1829 ic.Item.AssetType = (int) AssetType.ImageJPEG; 2141 ic.Item.AssetType = (int) AssetType.ImageJPEG;
@@ -1832,7 +2144,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
1832 break; 2144 break;
1833 case "jpg" : 2145 case "jpg" :
1834 case "jpeg" : 2146 case "jpeg" :
1835 Rest.Log.DebugFormat("{0} Type {1} inferred", 2147 Rest.Log.DebugFormat("{0} Type {1} inferred",
1836 MsgId, parts[parts.Length - 1]); 2148 MsgId, parts[parts.Length - 1]);
1837 if (ic.Item.AssetType == (int) AssetType.Unknown) 2149 if (ic.Item.AssetType == (int) AssetType.Unknown)
1838 ic.Item.AssetType = (int) AssetType.ImageJPEG; 2150 ic.Item.AssetType = (int) AssetType.ImageJPEG;
@@ -1873,19 +2185,22 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
1873 temp = OpenJPEGNet.LoadTGAClass.LoadTGA(tgadata); 2185 temp = OpenJPEGNet.LoadTGAClass.LoadTGA(tgadata);
1874 ic.Asset.Data = OpenJPEGNet.OpenJPEG.EncodeFromImage(temp, true); 2186 ic.Asset.Data = OpenJPEGNet.OpenJPEG.EncodeFromImage(temp, true);
1875 } 2187 }
1876 2188
1877 ic.reset(); 2189 ic.reset();
2190
1878 } 2191 }
1879 2192
1880 #region Inventory RequestData extension 2193 #region Inventory RequestData extension
1881 2194
1882 internal class InventoryRequestData : RequestData 2195 internal class InventoryRequestData : RequestData
1883 { 2196 {
2197
1884 /// <summary> 2198 /// <summary>
1885 /// These are the inventory specific request/response state 2199 /// These are the inventory specific request/response state
1886 /// extensions. 2200 /// extensions.
1887 /// </summary> 2201 /// </summary>
1888 2202
2203 internal LLUUID uuid = LLUUID.Zero;
1889 internal bool HaveInventory = false; 2204 internal bool HaveInventory = false;
1890 internal ICollection<InventoryFolderImpl> folders = null; 2205 internal ICollection<InventoryFolderImpl> folders = null;
1891 internal ICollection<InventoryItemBase> items = null; 2206 internal ICollection<InventoryItemBase> items = null;
@@ -1898,7 +2213,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
1898 } 2213 }
1899 2214
1900 /// <summary> 2215 /// <summary>
1901 /// This is the callback method required by inventory services. The 2216 /// This is the callback method required by inventory services. The
1902 /// requestor issues an inventory request and then blocks until this 2217 /// requestor issues an inventory request and then blocks until this
1903 /// method signals the monitor. 2218 /// method signals the monitor.
1904 /// </summary> 2219 /// </summary>
@@ -1914,6 +2229,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
1914 Monitor.Pulse(this); 2229 Monitor.Pulse(this);
1915 } 2230 }
1916 } 2231 }
2232
1917 } 2233 }
1918 2234
1919 #endregion Inventory RequestData extension 2235 #endregion Inventory RequestData extension
@@ -1926,6 +2242,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
1926 2242
1927 internal class XmlInventoryCollection : InventoryCollection 2243 internal class XmlInventoryCollection : InventoryCollection
1928 { 2244 {
2245
1929 internal InventoryRequestData rdata; 2246 internal InventoryRequestData rdata;
1930 private Stack<InventoryFolderBase> stk; 2247 private Stack<InventoryFolderBase> stk;
1931 2248
@@ -2014,10 +2331,11 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
2014 initPermissions(); 2331 initPermissions();
2015 } 2332 }
2016 2333
2017 internal void Fail(int code, string desc) 2334 internal void Fail(int code, string addendum)
2018 { 2335 {
2019 rdata.Fail(code, desc); 2336 rdata.Fail(code, addendum);
2020 } 2337 }
2338
2021 } 2339 }
2022 } 2340 }
2023} 2341}