aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Capabilities/Handlers/FetchInventory
diff options
context:
space:
mode:
authorDiva Canto2015-05-10 08:44:21 -0700
committerDiva Canto2015-05-10 08:44:21 -0700
commitf992ed0f400573a54cca0dc919284f88ae83540f (patch)
tree43f380e7f3d907afb4a5571927e2936891b95ed7 /OpenSim/Capabilities/Handlers/FetchInventory
parentFix mantis #7054: AO in Firestorm and Kokua stopped loading after new invento... (diff)
downloadopensim-SC_OLD-f992ed0f400573a54cca0dc919284f88ae83540f.zip
opensim-SC_OLD-f992ed0f400573a54cca0dc919284f88ae83540f.tar.gz
opensim-SC_OLD-f992ed0f400573a54cca0dc919284f88ae83540f.tar.bz2
opensim-SC_OLD-f992ed0f400573a54cca0dc919284f88ae83540f.tar.xz
Added unit tests for FetchInventoryDescendents http://wiki.secondlife.com/wiki/Linden_Lab_Official:Inventory_API#Fetch_Inventory_Descendents
Also, consolidated the location of the files that handle inventory capabilities.
Diffstat (limited to 'OpenSim/Capabilities/Handlers/FetchInventory')
-rw-r--r--OpenSim/Capabilities/Handlers/FetchInventory/FetchInvDescHandler.cs840
-rw-r--r--OpenSim/Capabilities/Handlers/FetchInventory/FetchInvDescServerConnector.cs82
-rw-r--r--OpenSim/Capabilities/Handlers/FetchInventory/FetchInventory2Handler.cs118
-rw-r--r--OpenSim/Capabilities/Handlers/FetchInventory/Tests/FetchInventoryDescendents2HandlerTests.cs217
4 files changed, 1257 insertions, 0 deletions
diff --git a/OpenSim/Capabilities/Handlers/FetchInventory/FetchInvDescHandler.cs b/OpenSim/Capabilities/Handlers/FetchInventory/FetchInvDescHandler.cs
new file mode 100644
index 0000000..810096f
--- /dev/null
+++ b/OpenSim/Capabilities/Handlers/FetchInventory/FetchInvDescHandler.cs
@@ -0,0 +1,840 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections;
30using System.Collections.Generic;
31using System.Reflection;
32using log4net;
33using Nini.Config;
34using OpenMetaverse;
35using OpenMetaverse.StructuredData;
36using OpenSim.Framework;
37using OpenSim.Framework.Capabilities;
38using OpenSim.Region.Framework.Interfaces;
39using OpenSim.Framework.Servers.HttpServer;
40using OpenSim.Services.Interfaces;
41using Caps = OpenSim.Framework.Capabilities.Caps;
42
43namespace OpenSim.Capabilities.Handlers
44{
45 public class FetchInvDescHandler
46 {
47 private static readonly ILog m_log =
48 LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
49
50 private IInventoryService m_InventoryService;
51 private ILibraryService m_LibraryService;
52// private object m_fetchLock = new Object();
53
54 public FetchInvDescHandler(IInventoryService invService, ILibraryService libService)
55 {
56 m_InventoryService = invService;
57 m_LibraryService = libService;
58 }
59
60
61 public string FetchInventoryDescendentsRequest(string request, string path, string param, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
62 {
63
64 // nasty temporary hack here, the linden client falsely
65 // identifies the uuid 00000000-0000-0000-0000-000000000000
66 // as a string which breaks us
67 //
68 // correctly mark it as a uuid
69 //
70 request = request.Replace("<string>00000000-0000-0000-0000-000000000000</string>", "<uuid>00000000-0000-0000-0000-000000000000</uuid>");
71
72 // another hack <integer>1</integer> results in a
73 // System.ArgumentException: Object type System.Int32 cannot
74 // be converted to target type: System.Boolean
75 //
76 request = request.Replace("<key>fetch_folders</key><integer>0</integer>", "<key>fetch_folders</key><boolean>0</boolean>");
77 request = request.Replace("<key>fetch_folders</key><integer>1</integer>", "<key>fetch_folders</key><boolean>1</boolean>");
78
79 Hashtable hash = new Hashtable();
80 try
81 {
82 hash = (Hashtable)LLSD.LLSDDeserialize(Utils.StringToBytes(request));
83 }
84 catch (LLSD.LLSDParseException e)
85 {
86 m_log.ErrorFormat("[WEB FETCH INV DESC HANDLER]: Fetch error: {0}{1}" + e.Message, e.StackTrace);
87 m_log.Error("Request: " + request);
88 }
89
90 ArrayList foldersrequested = (ArrayList)hash["folders"];
91
92 string response = "";
93 string bad_folders_response = "";
94
95 List<LLSDFetchInventoryDescendents> folders = new List<LLSDFetchInventoryDescendents>();
96 for (int i = 0; i < foldersrequested.Count; i++)
97 {
98 Hashtable inventoryhash = (Hashtable)foldersrequested[i];
99
100 LLSDFetchInventoryDescendents llsdRequest = new LLSDFetchInventoryDescendents();
101
102 try
103 {
104 LLSDHelpers.DeserialiseOSDMap(inventoryhash, llsdRequest);
105 }
106 catch (Exception e)
107 {
108 m_log.Debug("[WEB FETCH INV DESC HANDLER]: caught exception doing OSD deserialize" + e);
109 continue;
110 }
111
112 // Filter duplicate folder ids that bad viewers may send
113 if (folders.Find(f => f.folder_id == llsdRequest.folder_id) == null)
114 folders.Add(llsdRequest);
115 }
116
117 if (folders.Count > 0)
118 {
119 List<UUID> bad_folders = new List<UUID>();
120 List<InventoryCollectionWithDescendents> invcollSet = Fetch(folders, bad_folders);
121 //m_log.DebugFormat("[XXX]: Got {0} folders from a request of {1}", invcollSet.Count, folders.Count);
122
123 if (invcollSet == null)
124 {
125 m_log.DebugFormat("[WEB FETCH INV DESC HANDLER]: Multiple folder fetch failed. Trying old protocol.");
126 return FetchInventoryDescendentsRequest(foldersrequested, httpRequest, httpResponse);
127 }
128
129 string inventoryitemstr = string.Empty;
130 foreach (InventoryCollectionWithDescendents icoll in invcollSet)
131 {
132 LLSDInventoryDescendents reply = ToLLSD(icoll.Collection, icoll.Descendents);
133
134 inventoryitemstr = LLSDHelpers.SerialiseLLSDReply(reply);
135 inventoryitemstr = inventoryitemstr.Replace("<llsd><map><key>folders</key><array>", "");
136 inventoryitemstr = inventoryitemstr.Replace("</array></map></llsd>", "");
137
138 response += inventoryitemstr;
139 }
140
141 //m_log.DebugFormat("[WEB FETCH INV DESC HANDLER]: Bad folders {0}", string.Join(", ", bad_folders));
142 foreach (UUID bad in bad_folders)
143 bad_folders_response += "<uuid>" + bad + "</uuid>";
144 }
145
146 if (response.Length == 0)
147 {
148 /* Viewers expect a bad_folders array when not available */
149 if (bad_folders_response.Length != 0)
150 {
151 response = "<llsd><map><key>bad_folders</key><array>" + bad_folders_response + "</array></map></llsd>";
152 }
153 else
154 {
155 response = "<llsd><map><key>folders</key><array /></map></llsd>";
156 }
157 }
158 else
159 {
160 if (bad_folders_response.Length != 0)
161 {
162 response = "<llsd><map><key>folders</key><array>" + response + "</array><key>bad_folders</key><array>" + bad_folders_response + "</array></map></llsd>";
163 }
164 else
165 {
166 response = "<llsd><map><key>folders</key><array>" + response + "</array></map></llsd>";
167 }
168 }
169
170 //m_log.DebugFormat("[WEB FETCH INV DESC HANDLER]: Replying to CAPS fetch inventory request for {0} folders. Item count {1}", folders.Count, item_count);
171 //m_log.Debug("[WEB FETCH INV DESC HANDLER] " + response);
172
173 return response;
174
175 }
176
177 /// <summary>
178 /// Construct an LLSD reply packet to a CAPS inventory request
179 /// </summary>
180 /// <param name="invFetch"></param>
181 /// <returns></returns>
182 private LLSDInventoryDescendents FetchInventoryReply(LLSDFetchInventoryDescendents invFetch)
183 {
184 LLSDInventoryDescendents reply = new LLSDInventoryDescendents();
185 LLSDInventoryFolderContents contents = new LLSDInventoryFolderContents();
186 contents.agent_id = invFetch.owner_id;
187 contents.owner_id = invFetch.owner_id;
188 contents.folder_id = invFetch.folder_id;
189
190 reply.folders.Array.Add(contents);
191 InventoryCollection inv = new InventoryCollection();
192 inv.Folders = new List<InventoryFolderBase>();
193 inv.Items = new List<InventoryItemBase>();
194 int version = 0;
195 int descendents = 0;
196
197 inv
198 = Fetch(
199 invFetch.owner_id, invFetch.folder_id, invFetch.owner_id,
200 invFetch.fetch_folders, invFetch.fetch_items, invFetch.sort_order, out version, out descendents);
201
202 if (inv != null && inv.Folders != null)
203 {
204 foreach (InventoryFolderBase invFolder in inv.Folders)
205 {
206 contents.categories.Array.Add(ConvertInventoryFolder(invFolder));
207 }
208
209 descendents += inv.Folders.Count;
210 }
211
212 if (inv != null && inv.Items != null)
213 {
214 foreach (InventoryItemBase invItem in inv.Items)
215 {
216 contents.items.Array.Add(ConvertInventoryItem(invItem));
217 }
218 }
219
220 contents.descendents = descendents;
221 contents.version = version;
222
223 //m_log.DebugFormat(
224 // "[WEB FETCH INV DESC HANDLER]: Replying to request for folder {0} (fetch items {1}, fetch folders {2}) with {3} items and {4} folders for agent {5}",
225 // invFetch.folder_id,
226 // invFetch.fetch_items,
227 // invFetch.fetch_folders,
228 // contents.items.Array.Count,
229 // contents.categories.Array.Count,
230 // invFetch.owner_id);
231
232 return reply;
233 }
234
235 private LLSDInventoryDescendents ToLLSD(InventoryCollection inv, int descendents)
236 {
237 LLSDInventoryDescendents reply = new LLSDInventoryDescendents();
238 LLSDInventoryFolderContents contents = new LLSDInventoryFolderContents();
239 contents.agent_id = inv.OwnerID;
240 contents.owner_id = inv.OwnerID;
241 contents.folder_id = inv.FolderID;
242
243 reply.folders.Array.Add(contents);
244
245 if (inv.Folders != null)
246 {
247 foreach (InventoryFolderBase invFolder in inv.Folders)
248 {
249 contents.categories.Array.Add(ConvertInventoryFolder(invFolder));
250 }
251
252 descendents += inv.Folders.Count;
253 }
254
255 if (inv.Items != null)
256 {
257 foreach (InventoryItemBase invItem in inv.Items)
258 {
259 contents.items.Array.Add(ConvertInventoryItem(invItem));
260 }
261 }
262
263 contents.descendents = descendents;
264 contents.version = inv.Version;
265
266 return reply;
267 }
268 /// <summary>
269 /// Old style. Soon to be deprecated.
270 /// </summary>
271 /// <param name="request"></param>
272 /// <param name="httpRequest"></param>
273 /// <param name="httpResponse"></param>
274 /// <returns></returns>
275 [Obsolete]
276 private string FetchInventoryDescendentsRequest(ArrayList foldersrequested, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
277 {
278 //m_log.DebugFormat("[WEB FETCH INV DESC HANDLER]: Received request for {0} folders", foldersrequested.Count);
279
280 string response = "";
281 string bad_folders_response = "";
282
283 for (int i = 0; i < foldersrequested.Count; i++)
284 {
285 string inventoryitemstr = "";
286 Hashtable inventoryhash = (Hashtable)foldersrequested[i];
287
288 LLSDFetchInventoryDescendents llsdRequest = new LLSDFetchInventoryDescendents();
289
290 try
291 {
292 LLSDHelpers.DeserialiseOSDMap(inventoryhash, llsdRequest);
293 }
294 catch (Exception e)
295 {
296 m_log.Debug("[WEB FETCH INV DESC HANDLER]: caught exception doing OSD deserialize" + e);
297 }
298
299 LLSDInventoryDescendents reply = FetchInventoryReply(llsdRequest);
300
301 if (null == reply)
302 {
303 bad_folders_response += "<uuid>" + llsdRequest.folder_id.ToString() + "</uuid>";
304 }
305 else
306 {
307 inventoryitemstr = LLSDHelpers.SerialiseLLSDReply(reply);
308 inventoryitemstr = inventoryitemstr.Replace("<llsd><map><key>folders</key><array>", "");
309 inventoryitemstr = inventoryitemstr.Replace("</array></map></llsd>", "");
310 }
311
312 response += inventoryitemstr;
313 }
314
315 if (response.Length == 0)
316 {
317 /* Viewers expect a bad_folders array when not available */
318 if (bad_folders_response.Length != 0)
319 {
320 response = "<llsd><map><key>bad_folders</key><array>" + bad_folders_response + "</array></map></llsd>";
321 }
322 else
323 {
324 response = "<llsd><map><key>folders</key><array /></map></llsd>";
325 }
326 }
327 else
328 {
329 if (bad_folders_response.Length != 0)
330 {
331 response = "<llsd><map><key>folders</key><array>" + response + "</array><key>bad_folders</key><array>" + bad_folders_response + "</array></map></llsd>";
332 }
333 else
334 {
335 response = "<llsd><map><key>folders</key><array>" + response + "</array></map></llsd>";
336 }
337 }
338
339 // m_log.DebugFormat("[WEB FETCH INV DESC HANDLER]: Replying to CAPS fetch inventory request");
340 //m_log.Debug("[WEB FETCH INV DESC HANDLER] "+response);
341
342 return response;
343
344 // }
345 }
346
347 /// <summary>
348 /// Handle the caps inventory descendents fetch.
349 /// </summary>
350 /// <param name="agentID"></param>
351 /// <param name="folderID"></param>
352 /// <param name="ownerID"></param>
353 /// <param name="fetchFolders"></param>
354 /// <param name="fetchItems"></param>
355 /// <param name="sortOrder"></param>
356 /// <param name="version"></param>
357 /// <returns>An empty InventoryCollection if the inventory look up failed</returns>
358 [Obsolete]
359 private InventoryCollection Fetch(
360 UUID agentID, UUID folderID, UUID ownerID,
361 bool fetchFolders, bool fetchItems, int sortOrder, out int version, out int descendents)
362 {
363 //m_log.DebugFormat(
364 // "[WEB FETCH INV DESC HANDLER]: Fetching folders ({0}), items ({1}) from {2} for agent {3}",
365 // fetchFolders, fetchItems, folderID, agentID);
366
367 // FIXME MAYBE: We're not handling sortOrder!
368
369 version = 0;
370 descendents = 0;
371
372 InventoryFolderImpl fold;
373 if (m_LibraryService != null && m_LibraryService.LibraryRootFolder != null && agentID == m_LibraryService.LibraryRootFolder.Owner)
374 {
375 if ((fold = m_LibraryService.LibraryRootFolder.FindFolder(folderID)) != null)
376 {
377 InventoryCollection ret = new InventoryCollection();
378 ret.Folders = new List<InventoryFolderBase>();
379 ret.Items = fold.RequestListOfItems();
380 descendents = ret.Folders.Count + ret.Items.Count;
381
382 return ret;
383 }
384 }
385
386 InventoryCollection contents = new InventoryCollection();
387
388 if (folderID != UUID.Zero)
389 {
390 InventoryCollection fetchedContents = m_InventoryService.GetFolderContent(agentID, folderID);
391
392 if (fetchedContents == null)
393 {
394 m_log.WarnFormat("[WEB FETCH INV DESC HANDLER]: Could not get contents of folder {0} for user {1}", folderID, agentID);
395 return contents;
396 }
397 contents = fetchedContents;
398 InventoryFolderBase containingFolder = new InventoryFolderBase();
399 containingFolder.ID = folderID;
400 containingFolder.Owner = agentID;
401 containingFolder = m_InventoryService.GetFolder(containingFolder);
402
403 if (containingFolder != null)
404 {
405 //m_log.DebugFormat(
406 // "[WEB FETCH INV DESC HANDLER]: Retrieved folder {0} {1} for agent id {2}",
407 // containingFolder.Name, containingFolder.ID, agentID);
408
409 version = containingFolder.Version;
410
411 if (fetchItems)
412 {
413 List<InventoryItemBase> itemsToReturn = contents.Items;
414 List<InventoryItemBase> originalItems = new List<InventoryItemBase>(itemsToReturn);
415
416 // descendents must only include the links, not the linked items we add
417 descendents = originalItems.Count;
418
419 // Add target items for links in this folder before the links themselves.
420 foreach (InventoryItemBase item in originalItems)
421 {
422 if (item.AssetType == (int)AssetType.Link)
423 {
424 InventoryItemBase linkedItem = m_InventoryService.GetItem(new InventoryItemBase(item.AssetID));
425
426 // Take care of genuinely broken links where the target doesn't exist
427 // HACK: Also, don't follow up links that just point to other links. In theory this is legitimate,
428 // but no viewer has been observed to set these up and this is the lazy way of avoiding cycles
429 // rather than having to keep track of every folder requested in the recursion.
430 if (linkedItem != null && linkedItem.AssetType != (int)AssetType.Link)
431 itemsToReturn.Insert(0, linkedItem);
432 }
433 }
434
435 // Now scan for folder links and insert the items they target and those links at the head of the return data
436 foreach (InventoryItemBase item in originalItems)
437 {
438 if (item.AssetType == (int)AssetType.LinkFolder)
439 {
440 InventoryCollection linkedFolderContents = m_InventoryService.GetFolderContent(ownerID, item.AssetID);
441 List<InventoryItemBase> links = linkedFolderContents.Items;
442
443 itemsToReturn.InsertRange(0, links);
444
445 foreach (InventoryItemBase link in linkedFolderContents.Items)
446 {
447 // Take care of genuinely broken links where the target doesn't exist
448 // HACK: Also, don't follow up links that just point to other links. In theory this is legitimate,
449 // but no viewer has been observed to set these up and this is the lazy way of avoiding cycles
450 // rather than having to keep track of every folder requested in the recursion.
451 if (link != null)
452 {
453// m_log.DebugFormat(
454// "[WEB FETCH INV DESC HANDLER]: Adding item {0} {1} from folder {2} linked from {3}",
455// link.Name, (AssetType)link.AssetType, item.AssetID, containingFolder.Name);
456
457 InventoryItemBase linkedItem
458 = m_InventoryService.GetItem(new InventoryItemBase(link.AssetID));
459
460 if (linkedItem != null)
461 itemsToReturn.Insert(0, linkedItem);
462 }
463 }
464 }
465 }
466 }
467
468// foreach (InventoryItemBase item in contents.Items)
469// {
470// m_log.DebugFormat(
471// "[WEB FETCH INV DESC HANDLER]: Returning item {0}, type {1}, parent {2} in {3} {4}",
472// item.Name, (AssetType)item.AssetType, item.Folder, containingFolder.Name, containingFolder.ID);
473// }
474
475 // =====
476
477//
478// foreach (InventoryItemBase linkedItem in linkedItemsToAdd)
479// {
480// m_log.DebugFormat(
481// "[WEB FETCH INV DESC HANDLER]: Inserted linked item {0} for link in folder {1} for agent {2}",
482// linkedItem.Name, folderID, agentID);
483//
484// contents.Items.Add(linkedItem);
485// }
486//
487// // If the folder requested contains links, then we need to send those folders first, otherwise the links
488// // will be broken in the viewer.
489// HashSet<UUID> linkedItemFolderIdsToSend = new HashSet<UUID>();
490// foreach (InventoryItemBase item in contents.Items)
491// {
492// if (item.AssetType == (int)AssetType.Link)
493// {
494// InventoryItemBase linkedItem = m_InventoryService.GetItem(new InventoryItemBase(item.AssetID));
495//
496// // Take care of genuinely broken links where the target doesn't exist
497// // HACK: Also, don't follow up links that just point to other links. In theory this is legitimate,
498// // but no viewer has been observed to set these up and this is the lazy way of avoiding cycles
499// // rather than having to keep track of every folder requested in the recursion.
500// if (linkedItem != null && linkedItem.AssetType != (int)AssetType.Link)
501// {
502// // We don't need to send the folder if source and destination of the link are in the same
503// // folder.
504// if (linkedItem.Folder != containingFolder.ID)
505// linkedItemFolderIdsToSend.Add(linkedItem.Folder);
506// }
507// }
508// }
509//
510// foreach (UUID linkedItemFolderId in linkedItemFolderIdsToSend)
511// {
512// m_log.DebugFormat(
513// "[WEB FETCH INV DESC HANDLER]: Recursively fetching folder {0} linked by item in folder {1} for agent {2}",
514// linkedItemFolderId, folderID, agentID);
515//
516// int dummyVersion;
517// InventoryCollection linkedCollection
518// = Fetch(
519// agentID, linkedItemFolderId, ownerID, fetchFolders, fetchItems, sortOrder, out dummyVersion);
520//
521// InventoryFolderBase linkedFolder = new InventoryFolderBase(linkedItemFolderId);
522// linkedFolder.Owner = agentID;
523// linkedFolder = m_InventoryService.GetFolder(linkedFolder);
524//
525//// contents.Folders.AddRange(linkedCollection.Folders);
526//
527// contents.Folders.Add(linkedFolder);
528// contents.Items.AddRange(linkedCollection.Items);
529// }
530// }
531 }
532 }
533 else
534 {
535 // Lost items don't really need a version
536 version = 1;
537 }
538
539 return contents;
540
541 }
542
543 private void AddLibraryFolders(List<LLSDFetchInventoryDescendents> fetchFolders, List<InventoryCollectionWithDescendents> result)
544 {
545 InventoryFolderImpl fold;
546 if (m_LibraryService != null && m_LibraryService.LibraryRootFolder != null)
547 {
548 List<LLSDFetchInventoryDescendents> libfolders = fetchFolders.FindAll(f => f.owner_id == m_LibraryService.LibraryRootFolder.Owner);
549 fetchFolders.RemoveAll(f => libfolders.Contains(f));
550
551 //m_log.DebugFormat("[XXX]: Found {0} library folders in request", libfolders.Count);
552
553 foreach (LLSDFetchInventoryDescendents f in libfolders)
554 {
555 if ((fold = m_LibraryService.LibraryRootFolder.FindFolder(f.folder_id)) != null)
556 {
557 InventoryCollectionWithDescendents ret = new InventoryCollectionWithDescendents();
558 ret.Collection = new InventoryCollection();
559 ret.Collection.Folders = new List<InventoryFolderBase>();
560 ret.Collection.Items = fold.RequestListOfItems();
561 ret.Collection.OwnerID = m_LibraryService.LibraryRootFolder.Owner;
562 ret.Collection.FolderID = f.folder_id;
563 ret.Collection.Version = fold.Version;
564
565 ret.Descendents = ret.Collection.Items.Count;
566 result.Add(ret);
567
568 //m_log.DebugFormat("[XXX]: Added libfolder {0} ({1}) {2}", ret.Collection.FolderID, ret.Collection.OwnerID);
569 }
570 }
571 }
572 }
573
574 private List<InventoryCollectionWithDescendents> Fetch(List<LLSDFetchInventoryDescendents> fetchFolders, List<UUID> bad_folders)
575 {
576 //m_log.DebugFormat(
577 // "[WEB FETCH INV DESC HANDLER]: Fetching {0} folders for owner {1}", fetchFolders.Count, fetchFolders[0].owner_id);
578
579 // FIXME MAYBE: We're not handling sortOrder!
580
581 List<InventoryCollectionWithDescendents> result = new List<InventoryCollectionWithDescendents>();
582
583 AddLibraryFolders(fetchFolders, result);
584
585 if (fetchFolders.Count > 0)
586 {
587 UUID[] fids = new UUID[fetchFolders.Count];
588 int i = 0;
589 foreach (LLSDFetchInventoryDescendents f in fetchFolders)
590 fids[i++] = f.folder_id;
591
592 //m_log.DebugFormat("[XXX]: {0}", string.Join(",", fids));
593
594 InventoryCollection[] fetchedContents = m_InventoryService.GetMultipleFoldersContent(fetchFolders[0].owner_id, fids);
595
596 if (fetchedContents == null || (fetchedContents != null && fetchedContents.Length == 0))
597 {
598 //m_log.WarnFormat("[WEB FETCH INV DESC HANDLER]: Could not get contents of multiple folders for user {0}", fetchFolders[0].owner_id);
599 return null;
600 }
601
602 i = 0;
603 // Do some post-processing. May need to fetch more from inv server for links
604 foreach (InventoryCollection contents in fetchedContents)
605 {
606 // Find the original request
607 LLSDFetchInventoryDescendents freq = fetchFolders[i++];
608
609 InventoryCollectionWithDescendents coll = new InventoryCollectionWithDescendents();
610 coll.Collection = contents;
611
612 if (BadFolder(freq, contents, bad_folders))
613 continue;
614
615 // Next: link management
616 ProcessLinks(freq, coll);
617
618 result.Add(coll);
619 }
620 }
621
622 return result;
623 }
624
625 private bool BadFolder(LLSDFetchInventoryDescendents freq, InventoryCollection contents, List<UUID> bad_folders)
626 {
627 bool bad = false;
628 if (contents == null)
629 {
630 bad_folders.Add(freq.folder_id);
631 bad = true;
632 }
633
634 // The inventory server isn't sending FolderID in the collection...
635 // Must fetch it individually
636 if (contents.FolderID == UUID.Zero)
637 {
638 InventoryFolderBase containingFolder = new InventoryFolderBase();
639 containingFolder.ID = freq.folder_id;
640 containingFolder.Owner = freq.owner_id;
641 containingFolder = m_InventoryService.GetFolder(containingFolder);
642
643 if (containingFolder != null)
644 {
645 contents.FolderID = containingFolder.ID;
646 contents.OwnerID = containingFolder.Owner;
647 contents.Version = containingFolder.Version;
648 }
649 else
650 {
651 // Was it really a request for folder Zero?
652 // This is an overkill, but Firestorm really asks for folder Zero.
653 // I'm leaving the code here for the time being, but commented.
654 if (freq.folder_id == UUID.Zero)
655 {
656 //coll.Collection.OwnerID = freq.owner_id;
657 //coll.Collection.FolderID = contents.FolderID;
658 //containingFolder = m_InventoryService.GetRootFolder(freq.owner_id);
659 //if (containingFolder != null)
660 //{
661 // m_log.WarnFormat("[WEB FETCH INV DESC HANDLER]: Request for parent of folder {0}", containingFolder.ID);
662 // coll.Collection.Folders.Clear();
663 // coll.Collection.Folders.Add(containingFolder);
664 // if (m_LibraryService != null && m_LibraryService.LibraryRootFolder != null)
665 // {
666 // InventoryFolderBase lib = new InventoryFolderBase(m_LibraryService.LibraryRootFolder.ID, m_LibraryService.LibraryRootFolder.Owner);
667 // lib.Name = m_LibraryService.LibraryRootFolder.Name;
668 // lib.Type = m_LibraryService.LibraryRootFolder.Type;
669 // lib.Version = m_LibraryService.LibraryRootFolder.Version;
670 // coll.Collection.Folders.Add(lib);
671 // }
672 // coll.Collection.Items.Clear();
673 //}
674 }
675 else
676 {
677 m_log.WarnFormat("[WEB FETCH INV DESC HANDLER]: Unable to fetch folder {0}", freq.folder_id);
678 bad_folders.Add(freq.folder_id);
679 }
680 bad = true;
681 }
682 }
683
684 return bad;
685 }
686
687 private void ProcessLinks(LLSDFetchInventoryDescendents freq, InventoryCollectionWithDescendents coll)
688 {
689 InventoryCollection contents = coll.Collection;
690
691 if (freq.fetch_items && contents.Items != null)
692 {
693 List<InventoryItemBase> itemsToReturn = contents.Items;
694
695 // descendents must only include the links, not the linked items we add
696 coll.Descendents = itemsToReturn.Count;
697
698 // Add target items for links in this folder before the links themselves.
699 List<UUID> itemIDs = new List<UUID>();
700 List<UUID> folderIDs = new List<UUID>();
701 foreach (InventoryItemBase item in itemsToReturn)
702 {
703 //m_log.DebugFormat("[XXX]: {0} {1}", item.Name, item.AssetType);
704 if (item.AssetType == (int)AssetType.Link)
705 itemIDs.Add(item.AssetID);
706
707 else if (item.AssetType == (int)AssetType.LinkFolder)
708 folderIDs.Add(item.AssetID);
709 }
710
711 //m_log.DebugFormat("[XXX]: folder {0} has {1} links and {2} linkfolders", contents.FolderID, itemIDs.Count, folderIDs.Count);
712
713 // Scan for folder links and insert the items they target and those links at the head of the return data
714 if (folderIDs.Count > 0)
715 {
716 InventoryCollection[] linkedFolders = m_InventoryService.GetMultipleFoldersContent(coll.Collection.OwnerID, folderIDs.ToArray());
717 foreach (InventoryCollection linkedFolderContents in linkedFolders)
718 {
719 List<InventoryItemBase> links = linkedFolderContents.Items;
720
721 itemsToReturn.InsertRange(0, links);
722
723 foreach (InventoryItemBase link in linkedFolderContents.Items)
724 {
725 // Take care of genuinely broken links where the target doesn't exist
726 // HACK: Also, don't follow up links that just point to other links. In theory this is legitimate,
727 // but no viewer has been observed to set these up and this is the lazy way of avoiding cycles
728 // rather than having to keep track of every folder requested in the recursion.
729 if (link != null)
730 {
731 //m_log.DebugFormat(
732 // "[WEB FETCH INV DESC HANDLER]: Adding item {0} {1} from folder {2} linked from {3} ({4} {5})",
733 // link.Name, (AssetType)link.AssetType, linkedFolderContents.FolderID, contents.FolderID, link.ID, link.AssetID);
734 itemIDs.Add(link.ID);
735 }
736 }
737 }
738 }
739
740 if (itemIDs.Count > 0)
741 {
742 InventoryItemBase[] linked = m_InventoryService.GetMultipleItems(freq.owner_id, itemIDs.ToArray());
743 if (linked == null)
744 {
745 // OMG!!! One by one!!! This is fallback code, in case the backend isn't updated
746 m_log.WarnFormat("[WEB FETCH INV DESC HANDLER]: GetMultipleItems failed. Falling back to fetching inventory items one by one.");
747 linked = new InventoryItemBase[itemIDs.Count];
748 int i = 0;
749 InventoryItemBase item = new InventoryItemBase();
750 item.Owner = freq.owner_id;
751 foreach (UUID id in itemIDs)
752 {
753 item.ID = id;
754 linked[i++] = m_InventoryService.GetItem(item);
755 }
756 }
757
758 //m_log.DebugFormat("[WEB FETCH INV DESC HANDLER]: Processing folder {0}. Existing items:", freq.folder_id);
759 //foreach (InventoryItemBase item in itemsToReturn)
760 // m_log.DebugFormat("[XXX]: {0} {1} {2}", item.Name, item.AssetType, item.Folder);
761
762 if (linked != null)
763 {
764 foreach (InventoryItemBase linkedItem in linked)
765 {
766 // Take care of genuinely broken links where the target doesn't exist
767 // HACK: Also, don't follow up links that just point to other links. In theory this is legitimate,
768 // but no viewer has been observed to set these up and this is the lazy way of avoiding cycles
769 // rather than having to keep track of every folder requested in the recursion.
770 if (linkedItem != null && linkedItem.AssetType != (int)AssetType.Link)
771 {
772 itemsToReturn.Insert(0, linkedItem);
773 //m_log.DebugFormat("[WEB FETCH INV DESC HANDLER]: Added {0} {1} {2}", linkedItem.Name, linkedItem.AssetType, linkedItem.Folder);
774 }
775 }
776 }
777 }
778 }
779
780 }
781
782 /// <summary>
783 /// Convert an internal inventory folder object into an LLSD object.
784 /// </summary>
785 /// <param name="invFolder"></param>
786 /// <returns></returns>
787 private LLSDInventoryFolder ConvertInventoryFolder(InventoryFolderBase invFolder)
788 {
789 LLSDInventoryFolder llsdFolder = new LLSDInventoryFolder();
790 llsdFolder.folder_id = invFolder.ID;
791 llsdFolder.parent_id = invFolder.ParentID;
792 llsdFolder.name = invFolder.Name;
793 llsdFolder.type = invFolder.Type;
794 llsdFolder.preferred_type = -1;
795
796 return llsdFolder;
797 }
798
799 /// <summary>
800 /// Convert an internal inventory item object into an LLSD object.
801 /// </summary>
802 /// <param name="invItem"></param>
803 /// <returns></returns>
804 private LLSDInventoryItem ConvertInventoryItem(InventoryItemBase invItem)
805 {
806 LLSDInventoryItem llsdItem = new LLSDInventoryItem();
807 llsdItem.asset_id = invItem.AssetID;
808 llsdItem.created_at = invItem.CreationDate;
809 llsdItem.desc = invItem.Description;
810 llsdItem.flags = (int)invItem.Flags;
811 llsdItem.item_id = invItem.ID;
812 llsdItem.name = invItem.Name;
813 llsdItem.parent_id = invItem.Folder;
814 llsdItem.type = invItem.AssetType;
815 llsdItem.inv_type = invItem.InvType;
816
817 llsdItem.permissions = new LLSDPermissions();
818 llsdItem.permissions.creator_id = invItem.CreatorIdAsUuid;
819 llsdItem.permissions.base_mask = (int)invItem.CurrentPermissions;
820 llsdItem.permissions.everyone_mask = (int)invItem.EveryOnePermissions;
821 llsdItem.permissions.group_id = invItem.GroupID;
822 llsdItem.permissions.group_mask = (int)invItem.GroupPermissions;
823 llsdItem.permissions.is_owner_group = invItem.GroupOwned;
824 llsdItem.permissions.next_owner_mask = (int)invItem.NextPermissions;
825 llsdItem.permissions.owner_id = invItem.Owner;
826 llsdItem.permissions.owner_mask = (int)invItem.CurrentPermissions;
827 llsdItem.sale_info = new LLSDSaleInfo();
828 llsdItem.sale_info.sale_price = invItem.SalePrice;
829 llsdItem.sale_info.sale_type = invItem.SaleType;
830
831 return llsdItem;
832 }
833 }
834
835 class InventoryCollectionWithDescendents
836 {
837 public InventoryCollection Collection;
838 public int Descendents;
839 }
840} \ No newline at end of file
diff --git a/OpenSim/Capabilities/Handlers/FetchInventory/FetchInvDescServerConnector.cs b/OpenSim/Capabilities/Handlers/FetchInventory/FetchInvDescServerConnector.cs
new file mode 100644
index 0000000..6fbe173
--- /dev/null
+++ b/OpenSim/Capabilities/Handlers/FetchInventory/FetchInvDescServerConnector.cs
@@ -0,0 +1,82 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using Nini.Config;
30using OpenSim.Server.Base;
31using OpenSim.Services.Interfaces;
32using OpenSim.Framework.Servers.HttpServer;
33using OpenSim.Server.Handlers.Base;
34using OpenMetaverse;
35
36namespace OpenSim.Capabilities.Handlers
37{
38 public class FetchInvDescServerConnector : ServiceConnector
39 {
40 private IInventoryService m_InventoryService;
41 private ILibraryService m_LibraryService;
42 private string m_ConfigName = "CapsService";
43
44 public FetchInvDescServerConnector(IConfigSource config, IHttpServer server, string configName) :
45 base(config, server, configName)
46 {
47 if (configName != String.Empty)
48 m_ConfigName = configName;
49
50 IConfig serverConfig = config.Configs[m_ConfigName];
51 if (serverConfig == null)
52 throw new Exception(String.Format("No section '{0}' in config file", m_ConfigName));
53
54 string invService = serverConfig.GetString("InventoryService", String.Empty);
55
56 if (invService == String.Empty)
57 throw new Exception("No InventoryService in config file");
58
59 Object[] args = new Object[] { config };
60 m_InventoryService =
61 ServerUtils.LoadPlugin<IInventoryService>(invService, args);
62
63 if (m_InventoryService == null)
64 throw new Exception(String.Format("Failed to load InventoryService from {0}; config is {1}", invService, m_ConfigName));
65
66 string libService = serverConfig.GetString("LibraryService", String.Empty);
67 m_LibraryService =
68 ServerUtils.LoadPlugin<ILibraryService>(libService, args);
69
70 FetchInvDescHandler webFetchHandler = new FetchInvDescHandler(m_InventoryService, m_LibraryService);
71 IRequestHandler reqHandler
72 = new RestStreamHandler(
73 "POST",
74 "/CAPS/WebFetchInvDesc/" /*+ UUID.Random()*/,
75 webFetchHandler.FetchInventoryDescendentsRequest,
76 "FetchInvDescendents",
77 null);
78 server.AddStreamHandler(reqHandler);
79 }
80
81 }
82}
diff --git a/OpenSim/Capabilities/Handlers/FetchInventory/FetchInventory2Handler.cs b/OpenSim/Capabilities/Handlers/FetchInventory/FetchInventory2Handler.cs
new file mode 100644
index 0000000..b67b326
--- /dev/null
+++ b/OpenSim/Capabilities/Handlers/FetchInventory/FetchInventory2Handler.cs
@@ -0,0 +1,118 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using OpenMetaverse;
29using OpenMetaverse.StructuredData;
30using OpenSim.Framework;
31using OpenSim.Framework.Capabilities;
32using OpenSim.Framework.Servers.HttpServer;
33using OpenSim.Services.Interfaces;
34using OSDArray = OpenMetaverse.StructuredData.OSDArray;
35using OSDMap = OpenMetaverse.StructuredData.OSDMap;
36
37namespace OpenSim.Capabilities.Handlers
38{
39 public class FetchInventory2Handler
40 {
41// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
42
43 private IInventoryService m_inventoryService;
44 private UUID m_agentID;
45
46 public FetchInventory2Handler(IInventoryService invService, UUID agentId)
47 {
48 m_inventoryService = invService;
49 m_agentID = agentId;
50 }
51
52 public string FetchInventoryRequest(string request, string path, string param, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
53 {
54// m_log.DebugFormat("[FETCH INVENTORY HANDLER]: Received FetchInventory capabilty request");
55
56 OSDMap requestmap = (OSDMap)OSDParser.DeserializeLLSDXml(Utils.StringToBytes(request));
57 OSDArray itemsRequested = (OSDArray)requestmap["items"];
58
59 string reply;
60 LLSDFetchInventory llsdReply = new LLSDFetchInventory();
61
62 foreach (OSDMap osdItemId in itemsRequested)
63 {
64 UUID itemId = osdItemId["item_id"].AsUUID();
65
66 InventoryItemBase item = m_inventoryService.GetItem(new InventoryItemBase(itemId, m_agentID));
67
68 if (item != null)
69 {
70 // We don't know the agent that this request belongs to so we'll use the agent id of the item
71 // which will be the same for all items.
72 llsdReply.agent_id = item.Owner;
73
74 llsdReply.items.Array.Add(ConvertInventoryItem(item));
75 }
76 }
77
78 reply = LLSDHelpers.SerialiseLLSDReply(llsdReply);
79
80 return reply;
81 }
82
83 /// <summary>
84 /// Convert an internal inventory item object into an LLSD object.
85 /// </summary>
86 /// <param name="invItem"></param>
87 /// <returns></returns>
88 private LLSDInventoryItem ConvertInventoryItem(InventoryItemBase invItem)
89 {
90 LLSDInventoryItem llsdItem = new LLSDInventoryItem();
91 llsdItem.asset_id = invItem.AssetID;
92 llsdItem.created_at = invItem.CreationDate;
93 llsdItem.desc = invItem.Description;
94 llsdItem.flags = (int)invItem.Flags;
95 llsdItem.item_id = invItem.ID;
96 llsdItem.name = invItem.Name;
97 llsdItem.parent_id = invItem.Folder;
98 llsdItem.type = invItem.AssetType;
99 llsdItem.inv_type = invItem.InvType;
100
101 llsdItem.permissions = new LLSDPermissions();
102 llsdItem.permissions.creator_id = invItem.CreatorIdAsUuid;
103 llsdItem.permissions.base_mask = (int)invItem.CurrentPermissions;
104 llsdItem.permissions.everyone_mask = (int)invItem.EveryOnePermissions;
105 llsdItem.permissions.group_id = invItem.GroupID;
106 llsdItem.permissions.group_mask = (int)invItem.GroupPermissions;
107 llsdItem.permissions.is_owner_group = invItem.GroupOwned;
108 llsdItem.permissions.next_owner_mask = (int)invItem.NextPermissions;
109 llsdItem.permissions.owner_id = invItem.Owner;
110 llsdItem.permissions.owner_mask = (int)invItem.CurrentPermissions;
111 llsdItem.sale_info = new LLSDSaleInfo();
112 llsdItem.sale_info.sale_price = invItem.SalePrice;
113 llsdItem.sale_info.sale_type = invItem.SaleType;
114
115 return llsdItem;
116 }
117 }
118} \ No newline at end of file
diff --git a/OpenSim/Capabilities/Handlers/FetchInventory/Tests/FetchInventoryDescendents2HandlerTests.cs b/OpenSim/Capabilities/Handlers/FetchInventory/Tests/FetchInventoryDescendents2HandlerTests.cs
new file mode 100644
index 0000000..1064c7b
--- /dev/null
+++ b/OpenSim/Capabilities/Handlers/FetchInventory/Tests/FetchInventoryDescendents2HandlerTests.cs
@@ -0,0 +1,217 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Linq;
31using System.Net;
32using log4net;
33using log4net.Config;
34using NUnit.Framework;
35using OpenMetaverse;
36using OpenSim.Capabilities.Handlers;
37using OpenSim.Framework;
38using OpenSim.Framework.Servers.HttpServer;
39using OpenSim.Region.Framework.Scenes;
40using OpenSim.Services.Interfaces;
41using OpenSim.Tests.Common;
42
43namespace OpenSim.Capabilities.Handlers.FetchInventory.Tests
44{
45 [TestFixture]
46 public class FetchInventoryDescendents2HandlerTests : OpenSimTestCase
47 {
48 private UUID m_userID = UUID.Zero;
49 private Scene m_scene;
50 private UUID m_rootFolderID;
51 private int m_rootDescendents;
52 private UUID m_notecardsFolder;
53 private UUID m_objectsFolder;
54
55 private void Init()
56 {
57 // CreateInventoryFolder an inventory
58
59 m_scene = new SceneHelpers().SetupScene();
60
61 m_scene.InventoryService.CreateUserInventory(m_userID);
62
63 m_rootFolderID = m_scene.InventoryService.GetRootFolder(m_userID).ID;
64
65 InventoryFolderBase of = m_scene.InventoryService.GetFolderForType(m_userID, AssetType.Object);
66 m_objectsFolder = of.ID;
67
68 // Add an object
69 InventoryItemBase item = new InventoryItemBase(new UUID("b0000000-0000-0000-0000-00000000000b"), m_userID);
70 item.AssetID = UUID.Random();
71 item.AssetType = (int)AssetType.Object;
72 item.Folder = m_objectsFolder;
73 item.Name = "Some Object";
74 m_scene.InventoryService.AddItem(item);
75
76 InventoryFolderBase ncf = m_scene.InventoryService.GetFolderForType(m_userID, AssetType.Notecard);
77 m_notecardsFolder = ncf.ID;
78
79 // Add a notecard
80 item = new InventoryItemBase(new UUID("10000000-0000-0000-0000-000000000001"), m_userID);
81 item.AssetID = UUID.Random();
82 item.AssetType = (int)AssetType.Notecard;
83 item.Folder = m_notecardsFolder;
84 item.Name = "Test Notecard 1";
85 m_scene.InventoryService.AddItem(item);
86 // Add another notecard
87 item.ID = new UUID("20000000-0000-0000-0000-000000000002");
88 item.AssetID = new UUID("a0000000-0000-0000-0000-00000000000a");
89 item.Name = "Test Notecard 2";
90 m_scene.InventoryService.AddItem(item);
91
92 // Add a folder
93 InventoryFolderBase folder = new InventoryFolderBase(new UUID("f0000000-0000-0000-0000-00000000000f"), "Test Folder", m_userID, m_rootFolderID);
94 m_scene.InventoryService.AddFolder(folder);
95
96 // Add a link to notecard 2 in Test Folder
97 item.AssetID = item.ID; // use item ID of notecard 2
98 item.ID = new UUID("40000000-0000-0000-0000-000000000004");
99 item.AssetType = (int)AssetType.Link;
100 item.Folder = folder.ID;
101 item.Name = "Link to notecard";
102 m_scene.InventoryService.AddItem(item);
103
104 // Add a link to the Objects folder in Test Folder
105 item.AssetID = m_scene.InventoryService.GetFolderForType(m_userID, AssetType.Object).ID; // use item ID of Objects folder
106 item.ID = new UUID("50000000-0000-0000-0000-000000000005");
107 item.AssetType = (int)AssetType.LinkFolder;
108 item.Folder = folder.ID;
109 item.Name = "Link to Objects folder";
110 m_scene.InventoryService.AddItem(item);
111
112 InventoryCollection coll = m_scene.InventoryService.GetFolderContent(m_userID, m_rootFolderID);
113 m_rootDescendents = coll.Items.Count + coll.Folders.Count;
114 Console.WriteLine("Number of descendents: " + m_rootDescendents);
115 }
116
117 [Test]
118 public void Test_001_SimpleFolder()
119 {
120 TestHelpers.InMethod();
121
122 Init();
123
124 FetchInvDescHandler handler = new FetchInvDescHandler(m_scene.InventoryService, null);
125 TestOSHttpRequest req = new TestOSHttpRequest();
126 TestOSHttpResponse resp = new TestOSHttpResponse();
127
128 string request = "<llsd><map><key>folders</key><array><map><key>fetch_folders</key><integer>1</integer><key>fetch_items</key><boolean>1</boolean><key>folder_id</key><uuid>";
129 request += m_rootFolderID;
130 request += "</uuid><key>owner_id</key><uuid>00000000-0000-0000-0000-000000000000</uuid><key>sort_order</key><integer>1</integer></map></array></map></llsd>";
131
132 string llsdresponse = handler.FetchInventoryDescendentsRequest(request, "/FETCH", string.Empty, req, resp);
133
134 Assert.That(llsdresponse != null, Is.True, "Incorrect null response");
135 Assert.That(llsdresponse != string.Empty, Is.True, "Incorrect empty response");
136 Assert.That(llsdresponse.Contains("00000000-0000-0000-0000-000000000000"), Is.True, "Response should contain userID");
137
138 string descendents = "descendents</key><integer>" + m_rootDescendents + "</integer>";
139 Assert.That(llsdresponse.Contains(descendents), Is.True, "Incorrect number of descendents");
140 Console.WriteLine(llsdresponse);
141 }
142
143 [Test]
144 public void Test_002_MultipleFolders()
145 {
146 TestHelpers.InMethod();
147
148 FetchInvDescHandler handler = new FetchInvDescHandler(m_scene.InventoryService, null);
149 TestOSHttpRequest req = new TestOSHttpRequest();
150 TestOSHttpResponse resp = new TestOSHttpResponse();
151
152 string request = "<llsd><map><key>folders</key><array>";
153 request += "<map><key>fetch_folders</key><integer>1</integer><key>fetch_items</key><boolean>1</boolean><key>folder_id</key><uuid>";
154 request += m_rootFolderID;
155 request += "</uuid><key>owner_id</key><uuid>00000000-0000-0000-0000-000000000000</uuid><key>sort_order</key><integer>1</integer></map>";
156 request += "<map><key>fetch_folders</key><integer>1</integer><key>fetch_items</key><boolean>1</boolean><key>folder_id</key><uuid>";
157 request += m_notecardsFolder;
158 request += "</uuid><key>owner_id</key><uuid>00000000-0000-0000-0000-000000000000</uuid><key>sort_order</key><integer>1</integer></map>";
159 request += "</array></map></llsd>";
160
161 string llsdresponse = handler.FetchInventoryDescendentsRequest(request, "/FETCH", string.Empty, req, resp);
162 Console.WriteLine(llsdresponse);
163
164 string descendents = "descendents</key><integer>" + m_rootDescendents + "</integer>";
165 Assert.That(llsdresponse.Contains(descendents), Is.True, "Incorrect number of descendents for root folder");
166 descendents = "descendents</key><integer>2</integer>";
167 Assert.That(llsdresponse.Contains(descendents), Is.True, "Incorrect number of descendents for Notecard folder");
168
169 Assert.That(llsdresponse.Contains("10000000-0000-0000-0000-000000000001"), Is.True, "Notecard 1 is missing from response");
170 Assert.That(llsdresponse.Contains("20000000-0000-0000-0000-000000000002"), Is.True, "Notecard 2 is missing from response");
171 }
172
173 [Test]
174 public void Test_003_Links()
175 {
176 TestHelpers.InMethod();
177
178 FetchInvDescHandler handler = new FetchInvDescHandler(m_scene.InventoryService, null);
179 TestOSHttpRequest req = new TestOSHttpRequest();
180 TestOSHttpResponse resp = new TestOSHttpResponse();
181
182 string request = "<llsd><map><key>folders</key><array><map><key>fetch_folders</key><integer>1</integer><key>fetch_items</key><boolean>1</boolean><key>folder_id</key><uuid>";
183 request += "f0000000-0000-0000-0000-00000000000f";
184 request += "</uuid><key>owner_id</key><uuid>00000000-0000-0000-0000-000000000000</uuid><key>sort_order</key><integer>1</integer></map></array></map></llsd>";
185
186 string llsdresponse = handler.FetchInventoryDescendentsRequest(request, "/FETCH", string.Empty, req, resp);
187 Console.WriteLine(llsdresponse);
188
189 string descendents = "descendents</key><integer>2</integer>";
190 Assert.That(llsdresponse.Contains(descendents), Is.True, "Incorrect number of descendents for Test Folder");
191
192 // Make sure that the note card link is included
193 Assert.That(llsdresponse.Contains("Link to notecard"), Is.True, "Link to notecard is missing");
194
195 //Make sure the notecard item itself is included
196 Assert.That(llsdresponse.Contains("Test Notecard 2"), Is.True, "Notecard 2 item (the source) is missing");
197
198 // Make sure that the source item is before the link item
199 int pos1 = llsdresponse.IndexOf("Test Notecard 2");
200 int pos2 = llsdresponse.IndexOf("Link to notecard");
201 Assert.Less(pos1, pos2, "Source of link is after link");
202
203 // Make sure the folder link is included
204 Assert.That(llsdresponse.Contains("Link to Objects folder"), Is.True, "Link to Objects folder is missing");
205
206 // Make sure the objects inside the Objects folder are included
207 // Note: I'm not entirely sure this is needed, but that's what I found in the implementation
208 Assert.That(llsdresponse.Contains("Some Object"), Is.True, "Some Object item (contents of the source) is missing");
209
210 // Make sure that the source item is before the link item
211 pos1 = llsdresponse.IndexOf("Some Object");
212 pos2 = llsdresponse.IndexOf("Link to Objects folder");
213 Assert.Less(pos1, pos2, "Contents of source of folder link is after folder link");
214 }
215 }
216
217} \ No newline at end of file