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