aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Capabilities/Handlers/FetchInventory/FetchInvDescHandler.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Capabilities/Handlers/FetchInventory/FetchInvDescHandler.cs')
-rw-r--r--OpenSim/Capabilities/Handlers/FetchInventory/FetchInvDescHandler.cs848
1 files changed, 848 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