diff options
author | Dr Scofield | 2008-08-20 10:11:11 +0000 |
---|---|---|
committer | Dr Scofield | 2008-08-20 10:11:11 +0000 |
commit | 5e83a758157520d48b15c725f5be2b196d2414e3 (patch) | |
tree | 42ab07bde2a984d06632abd30e6ad59877021f3f /OpenSim/ApplicationPlugins/Rest/Inventory/RestInventoryServices.cs | |
parent | Adding unit test for issue 2006, though it's commented out as those compiler (diff) | |
download | opensim-SC-5e83a758157520d48b15c725f5be2b196d2414e3.zip opensim-SC-5e83a758157520d48b15c725f5be2b196d2414e3.tar.gz opensim-SC-5e83a758157520d48b15c725f5be2b196d2414e3.tar.bz2 opensim-SC-5e83a758157520d48b15c725f5be2b196d2414e3.tar.xz |
From: Alan Webb <alan_webb@us.ibm.com>
cleanups of the REST inventory code.
Diffstat (limited to 'OpenSim/ApplicationPlugins/Rest/Inventory/RestInventoryServices.cs')
-rw-r--r-- | OpenSim/ApplicationPlugins/Rest/Inventory/RestInventoryServices.cs | 916 |
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 | ||
28 | using System; | 29 | using System; |
@@ -41,22 +42,30 @@ using Nini.Config; | |||
41 | 42 | ||
42 | namespace OpenSim.ApplicationPlugins.Rest.Inventory | 43 | namespace 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 | } |