aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--OpenSim/Capabilities/Handlers/FetchInventory/FetchInvDescHandler.cs848
-rw-r--r--OpenSim/Capabilities/Handlers/FetchInventory/FetchInvDescServerConnector.cs (renamed from OpenSim/Capabilities/Handlers/WebFetchInventoryDescendents/WebFetchInvDescServerConnector.cs)8
-rw-r--r--OpenSim/Capabilities/Handlers/FetchInventory/FetchInventory2Handler.cs (renamed from OpenSim/Capabilities/Handlers/FetchInventory2/FetchInventory2Handler.cs)41
-rw-r--r--OpenSim/Capabilities/Handlers/FetchInventory/Tests/FetchInventory2HandlerTests.cs170
-rw-r--r--OpenSim/Capabilities/Handlers/FetchInventory/Tests/FetchInventoryDescendents2HandlerTests.cs292
-rw-r--r--OpenSim/Capabilities/Handlers/FetchInventory2/FetchInventory2ServerConnector.cs71
-rw-r--r--OpenSim/Region/ClientStack/Linden/Caps/FetchInventory2Module.cs20
-rw-r--r--OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs145
8 files changed, 1465 insertions, 130 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/WebFetchInventoryDescendents/WebFetchInvDescServerConnector.cs b/OpenSim/Capabilities/Handlers/FetchInventory/FetchInvDescServerConnector.cs
index 5d86557..9dcfaa4 100644
--- a/OpenSim/Capabilities/Handlers/WebFetchInventoryDescendents/WebFetchInvDescServerConnector.cs
+++ b/OpenSim/Capabilities/Handlers/FetchInventory/FetchInvDescServerConnector.cs
@@ -35,13 +35,13 @@ using OpenMetaverse;
35 35
36namespace OpenSim.Capabilities.Handlers 36namespace OpenSim.Capabilities.Handlers
37{ 37{
38 public class WebFetchInvDescServerConnector : ServiceConnector 38 public class FetchInvDescServerConnector : ServiceConnector
39 { 39 {
40 private IInventoryService m_InventoryService; 40 private IInventoryService m_InventoryService;
41 private ILibraryService m_LibraryService; 41 private ILibraryService m_LibraryService;
42 private string m_ConfigName = "CapsService"; 42 private string m_ConfigName = "CapsService";
43 43
44 public WebFetchInvDescServerConnector(IConfigSource config, IHttpServer server, string configName) : 44 public FetchInvDescServerConnector(IConfigSource config, IHttpServer server, string configName) :
45 base(config, server, configName) 45 base(config, server, configName)
46 { 46 {
47 if (configName != String.Empty) 47 if (configName != String.Empty)
@@ -67,13 +67,13 @@ namespace OpenSim.Capabilities.Handlers
67 m_LibraryService = 67 m_LibraryService =
68 ServerUtils.LoadPlugin<ILibraryService>(libService, args); 68 ServerUtils.LoadPlugin<ILibraryService>(libService, args);
69 69
70 WebFetchInvDescHandler webFetchHandler = new WebFetchInvDescHandler(m_InventoryService, m_LibraryService); 70 FetchInvDescHandler webFetchHandler = new FetchInvDescHandler(m_InventoryService, m_LibraryService, null);
71 IRequestHandler reqHandler 71 IRequestHandler reqHandler
72 = new RestStreamHandler( 72 = new RestStreamHandler(
73 "POST", 73 "POST",
74 "/CAPS/WebFetchInvDesc/" /*+ UUID.Random()*/, 74 "/CAPS/WebFetchInvDesc/" /*+ UUID.Random()*/,
75 webFetchHandler.FetchInventoryDescendentsRequest, 75 webFetchHandler.FetchInventoryDescendentsRequest,
76 "WebFetchInvDesc", 76 "FetchInvDescendents",
77 null); 77 null);
78 server.AddStreamHandler(reqHandler); 78 server.AddStreamHandler(reqHandler);
79 } 79 }
diff --git a/OpenSim/Capabilities/Handlers/FetchInventory2/FetchInventory2Handler.cs b/OpenSim/Capabilities/Handlers/FetchInventory/FetchInventory2Handler.cs
index d1503ee..638e8bc 100644
--- a/OpenSim/Capabilities/Handlers/FetchInventory2/FetchInventory2Handler.cs
+++ b/OpenSim/Capabilities/Handlers/FetchInventory/FetchInventory2Handler.cs
@@ -25,39 +25,36 @@
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */ 26 */
27 27
28using System;
29using System.Collections;
30using System.Collections.Generic;
31using System.Reflection; 28using System.Reflection;
32using log4net;
33using Nini.Config;
34using OpenMetaverse; 29using OpenMetaverse;
35using OpenMetaverse.StructuredData; 30using OpenMetaverse.StructuredData;
36using OpenSim.Framework; 31using OpenSim.Framework;
37using OpenSim.Framework.Capabilities; 32using OpenSim.Framework.Capabilities;
38using OpenSim.Region.Framework.Interfaces;
39using OpenSim.Framework.Servers.HttpServer; 33using OpenSim.Framework.Servers.HttpServer;
40using OpenSim.Services.Interfaces; 34using OpenSim.Services.Interfaces;
41using Caps = OpenSim.Framework.Capabilities.Caps;
42using OSDArray = OpenMetaverse.StructuredData.OSDArray; 35using OSDArray = OpenMetaverse.StructuredData.OSDArray;
43using OSDMap = OpenMetaverse.StructuredData.OSDMap; 36using OSDMap = OpenMetaverse.StructuredData.OSDMap;
44 37
38using log4net;
39
45namespace OpenSim.Capabilities.Handlers 40namespace OpenSim.Capabilities.Handlers
46{ 41{
47 public class FetchInventory2Handler 42 public class FetchInventory2Handler
48 { 43 {
49// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 44 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
50 45
51 private IInventoryService m_inventoryService; 46 private IInventoryService m_inventoryService;
47 private UUID m_agentID;
52 48
53 public FetchInventory2Handler(IInventoryService invService) 49 public FetchInventory2Handler(IInventoryService invService, UUID agentId)
54 { 50 {
55 m_inventoryService = invService; 51 m_inventoryService = invService;
52 m_agentID = agentId;
56 } 53 }
57 54
58 public string FetchInventoryRequest(string request, string path, string param, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) 55 public string FetchInventoryRequest(string request, string path, string param, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
59 { 56 {
60// m_log.DebugFormat("[FETCH INVENTORY HANDLER]: Received FetchInventory capabilty request"); 57 //m_log.DebugFormat("[FETCH INVENTORY HANDLER]: Received FetchInventory capability request {0}", request);
61 58
62 OSDMap requestmap = (OSDMap)OSDParser.DeserializeLLSDXml(Utils.StringToBytes(request)); 59 OSDMap requestmap = (OSDMap)OSDParser.DeserializeLLSDXml(Utils.StringToBytes(request));
63 OSDArray itemsRequested = (OSDArray)requestmap["items"]; 60 OSDArray itemsRequested = (OSDArray)requestmap["items"];
@@ -65,12 +62,32 @@ namespace OpenSim.Capabilities.Handlers
65 string reply; 62 string reply;
66 LLSDFetchInventory llsdReply = new LLSDFetchInventory(); 63 LLSDFetchInventory llsdReply = new LLSDFetchInventory();
67 64
65 UUID[] itemIDs = new UUID[itemsRequested.Count];
66 int i = 0;
68 foreach (OSDMap osdItemId in itemsRequested) 67 foreach (OSDMap osdItemId in itemsRequested)
69 { 68 {
70 UUID itemId = osdItemId["item_id"].AsUUID(); 69 itemIDs[i++] = osdItemId["item_id"].AsUUID();
70 }
71
72 InventoryItemBase[] items = m_inventoryService.GetMultipleItems(m_agentID, itemIDs);
71 73
72 InventoryItemBase item = m_inventoryService.GetItem(new InventoryItemBase(itemId)); 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 }
73 88
89 foreach (InventoryItemBase item in items)
90 {
74 if (item != null) 91 if (item != null)
75 { 92 {
76 // We don't know the agent that this request belongs to so we'll use the agent id of the item 93 // We don't know the agent that this request belongs to so we'll use the agent id of the item
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
diff --git a/OpenSim/Capabilities/Handlers/FetchInventory2/FetchInventory2ServerConnector.cs b/OpenSim/Capabilities/Handlers/FetchInventory2/FetchInventory2ServerConnector.cs
deleted file mode 100644
index 5bab52f..0000000
--- a/OpenSim/Capabilities/Handlers/FetchInventory2/FetchInventory2ServerConnector.cs
+++ /dev/null
@@ -1,71 +0,0 @@
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 FetchInventory2ServerConnector : ServiceConnector
39 {
40 private IInventoryService m_InventoryService;
41 private string m_ConfigName = "CapsService";
42
43 public FetchInventory2ServerConnector(IConfigSource config, IHttpServer server, string configName)
44 : base(config, server, configName)
45 {
46 if (configName != String.Empty)
47 m_ConfigName = configName;
48
49 IConfig serverConfig = config.Configs[m_ConfigName];
50 if (serverConfig == null)
51 throw new Exception(String.Format("No section '{0}' in config file", m_ConfigName));
52
53 string invService = serverConfig.GetString("InventoryService", String.Empty);
54
55 if (invService == String.Empty)
56 throw new Exception("No InventoryService in config file");
57
58 Object[] args = new Object[] { config };
59 m_InventoryService = ServerUtils.LoadPlugin<IInventoryService>(invService, args);
60
61 if (m_InventoryService == null)
62 throw new Exception(String.Format("Failed to load InventoryService from {0}; config is {1}", invService, m_ConfigName));
63
64 FetchInventory2Handler fiHandler = new FetchInventory2Handler(m_InventoryService);
65 IRequestHandler reqHandler
66 = new RestStreamHandler(
67 "POST", "/CAPS/FetchInventory/", fiHandler.FetchInventoryRequest, "FetchInventory", null);
68 server.AddStreamHandler(reqHandler);
69 }
70 }
71}
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/FetchInventory2Module.cs b/OpenSim/Region/ClientStack/Linden/Caps/FetchInventory2Module.cs
index 87d3d1c..e0a11cc 100644
--- a/OpenSim/Region/ClientStack/Linden/Caps/FetchInventory2Module.cs
+++ b/OpenSim/Region/ClientStack/Linden/Caps/FetchInventory2Module.cs
@@ -25,20 +25,16 @@
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */ 26 */
27 27
28using System;
29using System.Collections;
30using System.Reflection;
31using log4net;
32using Nini.Config;
33using Mono.Addins; 28using Mono.Addins;
29using Nini.Config;
34using OpenMetaverse; 30using OpenMetaverse;
35using OpenSim.Framework; 31using OpenSim.Capabilities.Handlers;
36using OpenSim.Framework.Servers.HttpServer; 32using OpenSim.Framework.Servers.HttpServer;
37using OpenSim.Region.Framework.Interfaces; 33using OpenSim.Region.Framework.Interfaces;
38using OpenSim.Region.Framework.Scenes; 34using OpenSim.Region.Framework.Scenes;
39using OpenSim.Services.Interfaces; 35using OpenSim.Services.Interfaces;
36using System;
40using Caps = OpenSim.Framework.Capabilities.Caps; 37using Caps = OpenSim.Framework.Capabilities.Caps;
41using OpenSim.Capabilities.Handlers;
42 38
43namespace OpenSim.Region.ClientStack.Linden 39namespace OpenSim.Region.ClientStack.Linden
44{ 40{
@@ -58,8 +54,6 @@ namespace OpenSim.Region.ClientStack.Linden
58 54
59 private string m_fetchInventory2Url; 55 private string m_fetchInventory2Url;
60 56
61 private FetchInventory2Handler m_fetchHandler;
62
63 #region ISharedRegionModule Members 57 #region ISharedRegionModule Members
64 58
65 public void Initialise(IConfigSource source) 59 public void Initialise(IConfigSource source)
@@ -98,10 +92,6 @@ namespace OpenSim.Region.ClientStack.Linden
98 92
99 m_inventoryService = m_scene.InventoryService; 93 m_inventoryService = m_scene.InventoryService;
100 94
101 // We'll reuse the same handler for all requests.
102 if (m_fetchInventory2Url == "localhost")
103 m_fetchHandler = new FetchInventory2Handler(m_inventoryService);
104
105 m_scene.EventManager.OnRegisterCaps += RegisterCaps; 95 m_scene.EventManager.OnRegisterCaps += RegisterCaps;
106 } 96 }
107 97
@@ -131,9 +121,11 @@ namespace OpenSim.Region.ClientStack.Linden
131 { 121 {
132 capUrl = "/CAPS/" + UUID.Random(); 122 capUrl = "/CAPS/" + UUID.Random();
133 123
124 FetchInventory2Handler fetchHandler = new FetchInventory2Handler(m_inventoryService, agentID);
125
134 IRequestHandler reqHandler 126 IRequestHandler reqHandler
135 = new RestStreamHandler( 127 = new RestStreamHandler(
136 "POST", capUrl, m_fetchHandler.FetchInventoryRequest, capName, agentID.ToString()); 128 "POST", capUrl, fetchHandler.FetchInventoryRequest, capName, agentID.ToString());
137 129
138 caps.RegisterHandler(capName, reqHandler); 130 caps.RegisterHandler(capName, reqHandler);
139 } 131 }
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs
index 5f3e66a..2a252e1 100644
--- a/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs
+++ b/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs
@@ -66,7 +66,26 @@ namespace OpenSim.Region.ClientStack.Linden
66 66
67 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 67 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
68 68
69 private Scene m_scene; 69 /// <summary>
70 /// Control whether requests will be processed asynchronously.
71 /// </summary>
72 /// <remarks>
73 /// Defaults to true. Can currently not be changed once a region has been added to the module.
74 /// </remarks>
75 public bool ProcessQueuedRequestsAsync { get; private set; }
76
77 /// <summary>
78 /// Number of inventory requests processed by this module.
79 /// </summary>
80 /// <remarks>
81 /// It's the PollServiceRequestManager that actually sends completed requests back to the requester.
82 /// </remarks>
83 public static int ProcessedRequestsCount { get; set; }
84
85 private static Stat s_queuedRequestsStat;
86 private static Stat s_processedRequestsStat;
87
88 public Scene Scene { get; private set; }
70 89
71 private IInventoryService m_InventoryService; 90 private IInventoryService m_InventoryService;
72 private ILibraryService m_LibraryService; 91 private ILibraryService m_LibraryService;
@@ -76,7 +95,7 @@ namespace OpenSim.Region.ClientStack.Linden
76 private string m_fetchInventoryDescendents2Url; 95 private string m_fetchInventoryDescendents2Url;
77 private string m_webFetchInventoryDescendentsUrl; 96 private string m_webFetchInventoryDescendentsUrl;
78 97
79 private static WebFetchInvDescHandler m_webFetchHandler; 98 private static FetchInvDescHandler m_webFetchHandler;
80 99
81 private static Thread[] m_workerThreads = null; 100 private static Thread[] m_workerThreads = null;
82 101
@@ -85,6 +104,13 @@ namespace OpenSim.Region.ClientStack.Linden
85 104
86 #region ISharedRegionModule Members 105 #region ISharedRegionModule Members
87 106
107 public WebFetchInvDescModule() : this(true) {}
108
109 public WebFetchInvDescModule(bool processQueuedResultsAsync)
110 {
111 ProcessQueuedRequestsAsync = processQueuedResultsAsync;
112 }
113
88 public void Initialise(IConfigSource source) 114 public void Initialise(IConfigSource source)
89 { 115 {
90 IConfig config = source.Configs["ClientStack.LindenCaps"]; 116 IConfig config = source.Configs["ClientStack.LindenCaps"];
@@ -92,8 +118,9 @@ namespace OpenSim.Region.ClientStack.Linden
92 return; 118 return;
93 119
94 m_fetchInventoryDescendents2Url = config.GetString("Cap_FetchInventoryDescendents2", string.Empty); 120 m_fetchInventoryDescendents2Url = config.GetString("Cap_FetchInventoryDescendents2", string.Empty);
95 m_webFetchInventoryDescendentsUrl = config.GetString("Cap_WebFetchInventoryDescendents", string.Empty); 121// m_webFetchInventoryDescendentsUrl = config.GetString("Cap_WebFetchInventoryDescendents", string.Empty);
96 122
123// if (m_fetchInventoryDescendents2Url != string.Empty || m_webFetchInventoryDescendentsUrl != string.Empty)
97 if (m_fetchInventoryDescendents2Url != string.Empty || m_webFetchInventoryDescendentsUrl != string.Empty) 124 if (m_fetchInventoryDescendents2Url != string.Empty || m_webFetchInventoryDescendentsUrl != string.Empty)
98 { 125 {
99 m_Enabled = true; 126 m_Enabled = true;
@@ -105,7 +132,7 @@ namespace OpenSim.Region.ClientStack.Linden
105 if (!m_Enabled) 132 if (!m_Enabled)
106 return; 133 return;
107 134
108 m_scene = s; 135 Scene = s;
109 } 136 }
110 137
111 public void RemoveRegion(Scene s) 138 public void RemoveRegion(Scene s)
@@ -113,12 +140,23 @@ namespace OpenSim.Region.ClientStack.Linden
113 if (!m_Enabled) 140 if (!m_Enabled)
114 return; 141 return;
115 142
116 m_scene.EventManager.OnRegisterCaps -= RegisterCaps; 143 Scene.EventManager.OnRegisterCaps -= RegisterCaps;
117 144
118 foreach (Thread t in m_workerThreads) 145 StatsManager.DeregisterStat(s_processedRequestsStat);
119 Watchdog.AbortThread(t.ManagedThreadId); 146 StatsManager.DeregisterStat(s_queuedRequestsStat);
147
148 if (ProcessQueuedRequestsAsync)
149 {
150 if (m_workerThreads != null)
151 {
152 foreach (Thread t in m_workerThreads)
153 Watchdog.AbortThread(t.ManagedThreadId);
120 154
121 m_scene = null; 155 m_workerThreads = null;
156 }
157 }
158
159 Scene = null;
122 } 160 }
123 161
124 public void RegionLoaded(Scene s) 162 public void RegionLoaded(Scene s)
@@ -126,19 +164,51 @@ namespace OpenSim.Region.ClientStack.Linden
126 if (!m_Enabled) 164 if (!m_Enabled)
127 return; 165 return;
128 166
129 m_InventoryService = m_scene.InventoryService; 167 if (s_processedRequestsStat == null)
130 m_LibraryService = m_scene.LibraryService; 168 s_processedRequestsStat =
169 new Stat(
170 "ProcessedFetchInventoryRequests",
171 "Number of processed fetch inventory requests",
172 "These have not necessarily yet been dispatched back to the requester.",
173 "",
174 "inventory",
175 "httpfetch",
176 StatType.Pull,
177 MeasuresOfInterest.AverageChangeOverTime,
178 stat => { stat.Value = ProcessedRequestsCount; },
179 StatVerbosity.Debug);
180
181 if (s_queuedRequestsStat == null)
182 s_queuedRequestsStat =
183 new Stat(
184 "QueuedFetchInventoryRequests",
185 "Number of fetch inventory requests queued for processing",
186 "",
187 "",
188 "inventory",
189 "httpfetch",
190 StatType.Pull,
191 MeasuresOfInterest.AverageChangeOverTime,
192 stat => { stat.Value = m_queue.Count; },
193 StatVerbosity.Debug);
194
195 StatsManager.RegisterStat(s_processedRequestsStat);
196 StatsManager.RegisterStat(s_queuedRequestsStat);
197
198 m_InventoryService = Scene.InventoryService;
199 m_LibraryService = Scene.LibraryService;
131 200
132 // We'll reuse the same handler for all requests. 201 // We'll reuse the same handler for all requests.
133 m_webFetchHandler = new WebFetchInvDescHandler(m_InventoryService, m_LibraryService); 202 m_webFetchHandler = new FetchInvDescHandler(m_InventoryService, m_LibraryService, Scene);
134 203
135 m_scene.EventManager.OnRegisterCaps += RegisterCaps; 204 Scene.EventManager.OnRegisterCaps += RegisterCaps;
136 205
137 if (m_workerThreads == null) 206 int nworkers = 2; // was 2
207 if (ProcessQueuedRequestsAsync && m_workerThreads == null)
138 { 208 {
139 m_workerThreads = new Thread[2]; 209 m_workerThreads = new Thread[nworkers];
140 210
141 for (uint i = 0; i < 2; i++) 211 for (uint i = 0; i < nworkers; i++)
142 { 212 {
143 m_workerThreads[i] = WorkManager.StartThread(DoInventoryRequests, 213 m_workerThreads[i] = WorkManager.StartThread(DoInventoryRequests,
144 String.Format("InventoryWorkerThread{0}", i), 214 String.Format("InventoryWorkerThread{0}", i),
@@ -173,12 +243,12 @@ namespace OpenSim.Region.ClientStack.Linden
173 private Dictionary<UUID, Hashtable> responses = 243 private Dictionary<UUID, Hashtable> responses =
174 new Dictionary<UUID, Hashtable>(); 244 new Dictionary<UUID, Hashtable>();
175 245
176 private Scene m_scene; 246 private WebFetchInvDescModule m_module;
177 247
178 public PollServiceInventoryEventArgs(Scene scene, string url, UUID pId) : 248 public PollServiceInventoryEventArgs(WebFetchInvDescModule module, string url, UUID pId) :
179 base(null, url, null, null, null, pId, int.MaxValue) 249 base(null, url, null, null, null, pId, int.MaxValue)
180 { 250 {
181 m_scene = scene; 251 m_module = module;
182 252
183 HasEvents = (x, y) => { lock (responses) return responses.ContainsKey(x); }; 253 HasEvents = (x, y) => { lock (responses) return responses.ContainsKey(x); };
184 GetEvents = (x, y) => 254 GetEvents = (x, y) =>
@@ -198,12 +268,7 @@ namespace OpenSim.Region.ClientStack.Linden
198 268
199 Request = (x, y) => 269 Request = (x, y) =>
200 { 270 {
201 ScenePresence sp = m_scene.GetScenePresence(Id); 271 ScenePresence sp = m_module.Scene.GetScenePresence(Id);
202 if (sp == null)
203 {
204 m_log.ErrorFormat("[INVENTORY]: Unable to find ScenePresence for {0}", Id);
205 return;
206 }
207 272
208 aPollRequest reqinfo = new aPollRequest(); 273 aPollRequest reqinfo = new aPollRequest();
209 reqinfo.thepoll = this; 274 reqinfo.thepoll = this;
@@ -298,7 +363,13 @@ namespace OpenSim.Region.ClientStack.Linden
298 requestinfo.request["body"].ToString(), String.Empty, String.Empty, null, null); 363 requestinfo.request["body"].ToString(), String.Empty, String.Empty, null, null);
299 364
300 lock (responses) 365 lock (responses)
366 {
367 if (responses.ContainsKey(requestID))
368 m_log.WarnFormat("[FETCH INVENTORY DESCENDENTS2 MODULE]: Caught in the act of loosing responses! Please report this on mantis #7054");
301 responses[requestID] = response; 369 responses[requestID] = response;
370 }
371
372 WebFetchInvDescModule.ProcessedRequestsCount++;
302 } 373 }
303 } 374 }
304 375
@@ -322,7 +393,7 @@ namespace OpenSim.Region.ClientStack.Linden
322 capUrl = "/CAPS/" + UUID.Random() + "/"; 393 capUrl = "/CAPS/" + UUID.Random() + "/";
323 394
324 // Register this as a poll service 395 // Register this as a poll service
325 PollServiceInventoryEventArgs args = new PollServiceInventoryEventArgs(m_scene, capUrl, agentID); 396 PollServiceInventoryEventArgs args = new PollServiceInventoryEventArgs(this, capUrl, agentID);
326 args.Type = PollServiceEventArgs.EventType.Inventory; 397 args.Type = PollServiceEventArgs.EventType.Inventory;
327 398
328 caps.RegisterPollHandler(capName, args); 399 caps.RegisterPollHandler(capName, args);
@@ -331,7 +402,7 @@ namespace OpenSim.Region.ClientStack.Linden
331 else 402 else
332 { 403 {
333 capUrl = url; 404 capUrl = url;
334 IExternalCapsModule handler = m_scene.RequestModuleInterface<IExternalCapsModule>(); 405 IExternalCapsModule handler = Scene.RequestModuleInterface<IExternalCapsModule>();
335 if (handler != null) 406 if (handler != null)
336 handler.RegisterExternalUserCapsHandler(agentID,caps,capName,capUrl); 407 handler.RegisterExternalUserCapsHandler(agentID,caps,capName,capUrl);
337 else 408 else
@@ -360,10 +431,26 @@ namespace OpenSim.Region.ClientStack.Linden
360 { 431 {
361 Watchdog.UpdateThread(); 432 Watchdog.UpdateThread();
362 433
363 aPollRequest poolreq = m_queue.Dequeue(); 434 WaitProcessQueuedInventoryRequest();
435 }
436 }
437
438 public void WaitProcessQueuedInventoryRequest()
439 {
440 aPollRequest poolreq = m_queue.Dequeue();
364 441
365 if (poolreq != null && poolreq.thepoll != null) 442 if (poolreq != null && poolreq.thepoll != null)
443 {
444 try
445 {
366 poolreq.thepoll.Process(poolreq); 446 poolreq.thepoll.Process(poolreq);
447 }
448 catch (Exception e)
449 {
450 m_log.ErrorFormat(
451 "[INVENTORY]: Failed to process queued inventory request {0} for {1} in {2}. Exception {3}",
452 poolreq.reqID, poolreq.presence != null ? poolreq.presence.Name : "unknown", Scene.Name, e);
453 }
367 } 454 }
368 } 455 }
369 } 456 }