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