clockwerk-opensim-stable – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | vero | 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 | |||
28 | using System; |
||
29 | using System.Collections; |
||
30 | using System.Collections.Generic; |
||
31 | using System.Reflection; |
||
32 | using log4net; |
||
33 | using Nini.Config; |
||
34 | using OpenMetaverse; |
||
35 | using OpenMetaverse.StructuredData; |
||
36 | using OpenSim.Framework; |
||
37 | using OpenSim.Framework.Capabilities; |
||
38 | using OpenSim.Region.Framework.Interfaces; |
||
39 | using OpenSim.Framework.Servers.HttpServer; |
||
40 | using OpenSim.Services.Interfaces; |
||
41 | using Caps = OpenSim.Framework.Capabilities.Caps; |
||
42 | |||
43 | namespace OpenSim.Capabilities.Handlers |
||
44 | { |
||
45 | public class WebFetchInvDescHandler |
||
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 WebFetchInvDescHandler(IInventoryService invService, ILibraryService libService) |
||
55 | { |
||
56 | m_InventoryService = invService; |
||
57 | m_LibraryService = libService; |
||
58 | } |
||
59 | |||
60 | public string FetchInventoryDescendentsRequest(string request, string path, string param, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) |
||
61 | { |
||
62 | // lock (m_fetchLock) |
||
63 | // { |
||
64 | // m_log.DebugFormat("[WEB FETCH INV DESC HANDLER]: Received request {0}", request); |
||
65 | |||
66 | // nasty temporary hack here, the linden client falsely |
||
67 | // identifies the uuid 00000000-0000-0000-0000-000000000000 |
||
68 | // as a string which breaks us |
||
69 | // |
||
70 | // correctly mark it as a uuid |
||
71 | // |
||
72 | request = request.Replace("<string>00000000-0000-0000-0000-000000000000</string>", "<uuid>00000000-0000-0000-0000-000000000000</uuid>"); |
||
73 | |||
74 | // another hack <integer>1</integer> results in a |
||
75 | // System.ArgumentException: Object type System.Int32 cannot |
||
76 | // be converted to target type: System.Boolean |
||
77 | // |
||
78 | request = request.Replace("<key>fetch_folders</key><integer>0</integer>", "<key>fetch_folders</key><boolean>0</boolean>"); |
||
79 | request = request.Replace("<key>fetch_folders</key><integer>1</integer>", "<key>fetch_folders</key><boolean>1</boolean>"); |
||
80 | |||
81 | Hashtable hash = new Hashtable(); |
||
82 | try |
||
83 | { |
||
84 | hash = (Hashtable)LLSD.LLSDDeserialize(Utils.StringToBytes(request)); |
||
85 | } |
||
86 | catch (LLSD.LLSDParseException e) |
||
87 | { |
||
88 | m_log.ErrorFormat("[WEB FETCH INV DESC HANDLER]: Fetch error: {0}{1}" + e.Message, e.StackTrace); |
||
89 | m_log.Error("Request: " + request); |
||
90 | } |
||
91 | |||
92 | ArrayList foldersrequested = (ArrayList)hash["folders"]; |
||
93 | |||
94 | string response = ""; |
||
95 | |||
96 | for (int i = 0; i < foldersrequested.Count; i++) |
||
97 | { |
||
98 | string inventoryitemstr = ""; |
||
99 | Hashtable inventoryhash = (Hashtable)foldersrequested[i]; |
||
100 | |||
101 | LLSDFetchInventoryDescendents llsdRequest = new LLSDFetchInventoryDescendents(); |
||
102 | |||
103 | try |
||
104 | { |
||
105 | LLSDHelpers.DeserialiseOSDMap(inventoryhash, llsdRequest); |
||
106 | } |
||
107 | catch (Exception e) |
||
108 | { |
||
109 | m_log.Debug("[WEB FETCH INV DESC HANDLER]: caught exception doing OSD deserialize" + e); |
||
110 | } |
||
111 | LLSDInventoryDescendents reply = FetchInventoryReply(llsdRequest); |
||
112 | |||
113 | inventoryitemstr = LLSDHelpers.SerialiseLLSDReply(reply); |
||
114 | inventoryitemstr = inventoryitemstr.Replace("<llsd><map><key>folders</key><array>", ""); |
||
115 | inventoryitemstr = inventoryitemstr.Replace("</array></map></llsd>", ""); |
||
116 | |||
117 | response += inventoryitemstr; |
||
118 | } |
||
119 | |||
120 | if (response.Length == 0) |
||
121 | { |
||
122 | // Ter-guess: If requests fail a lot, the client seems to stop requesting descendants. |
||
123 | // Therefore, I'm concluding that the client only has so many threads available to do requests |
||
124 | // and when a thread stalls.. is stays stalled. |
||
125 | // Therefore we need to return something valid |
||
126 | response = "<llsd><map><key>folders</key><array /></map></llsd>"; |
||
127 | } |
||
128 | else |
||
129 | { |
||
130 | response = "<llsd><map><key>folders</key><array>" + response + "</array></map></llsd>"; |
||
131 | } |
||
132 | |||
133 | // m_log.DebugFormat("[WEB FETCH INV DESC HANDLER]: Replying to CAPS fetch inventory request"); |
||
134 | //m_log.Debug("[WEB FETCH INV DESC HANDLER] "+response); |
||
135 | |||
136 | return response; |
||
137 | |||
138 | // } |
||
139 | } |
||
140 | |||
141 | /// <summary> |
||
142 | /// Construct an LLSD reply packet to a CAPS inventory request |
||
143 | /// </summary> |
||
144 | /// <param name="invFetch"></param> |
||
145 | /// <returns></returns> |
||
146 | private LLSDInventoryDescendents FetchInventoryReply(LLSDFetchInventoryDescendents invFetch) |
||
147 | { |
||
148 | LLSDInventoryDescendents reply = new LLSDInventoryDescendents(); |
||
149 | LLSDInventoryFolderContents contents = new LLSDInventoryFolderContents(); |
||
150 | contents.agent_id = invFetch.owner_id; |
||
151 | contents.owner_id = invFetch.owner_id; |
||
152 | contents.folder_id = invFetch.folder_id; |
||
153 | |||
154 | reply.folders.Array.Add(contents); |
||
155 | InventoryCollection inv = new InventoryCollection(); |
||
156 | inv.Folders = new List<InventoryFolderBase>(); |
||
157 | inv.Items = new List<InventoryItemBase>(); |
||
158 | int version = 0; |
||
159 | int descendents = 0; |
||
160 | |||
161 | inv |
||
162 | = Fetch( |
||
163 | invFetch.owner_id, invFetch.folder_id, invFetch.owner_id, |
||
164 | invFetch.fetch_folders, invFetch.fetch_items, invFetch.sort_order, out version, out descendents); |
||
165 | |||
166 | if (inv != null && inv.Folders != null) |
||
167 | { |
||
168 | foreach (InventoryFolderBase invFolder in inv.Folders) |
||
169 | { |
||
170 | contents.categories.Array.Add(ConvertInventoryFolder(invFolder)); |
||
171 | } |
||
172 | |||
173 | descendents += inv.Folders.Count; |
||
174 | } |
||
175 | |||
176 | if (inv != null && inv.Items != null) |
||
177 | { |
||
178 | foreach (InventoryItemBase invItem in inv.Items) |
||
179 | { |
||
180 | contents.items.Array.Add(ConvertInventoryItem(invItem)); |
||
181 | } |
||
182 | } |
||
183 | |||
184 | contents.descendents = descendents; |
||
185 | contents.version = version; |
||
186 | |||
187 | // m_log.DebugFormat( |
||
188 | // "[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}", |
||
189 | // invFetch.folder_id, |
||
190 | // invFetch.fetch_items, |
||
191 | // invFetch.fetch_folders, |
||
192 | // contents.items.Array.Count, |
||
193 | // contents.categories.Array.Count, |
||
194 | // invFetch.owner_id); |
||
195 | |||
196 | return reply; |
||
197 | } |
||
198 | |||
199 | /// <summary> |
||
200 | /// Handle the caps inventory descendents fetch. |
||
201 | /// </summary> |
||
202 | /// <param name="agentID"></param> |
||
203 | /// <param name="folderID"></param> |
||
204 | /// <param name="ownerID"></param> |
||
205 | /// <param name="fetchFolders"></param> |
||
206 | /// <param name="fetchItems"></param> |
||
207 | /// <param name="sortOrder"></param> |
||
208 | /// <param name="version"></param> |
||
209 | /// <returns>An empty InventoryCollection if the inventory look up failed</returns> |
||
210 | private InventoryCollection Fetch( |
||
211 | UUID agentID, UUID folderID, UUID ownerID, |
||
212 | bool fetchFolders, bool fetchItems, int sortOrder, out int version, out int descendents) |
||
213 | { |
||
214 | // m_log.DebugFormat( |
||
215 | // "[WEB FETCH INV DESC HANDLER]: Fetching folders ({0}), items ({1}) from {2} for agent {3}", |
||
216 | // fetchFolders, fetchItems, folderID, agentID); |
||
217 | |||
218 | // FIXME MAYBE: We're not handling sortOrder! |
||
219 | |||
220 | version = 0; |
||
221 | descendents = 0; |
||
222 | |||
223 | InventoryFolderImpl fold; |
||
224 | if (m_LibraryService != null && m_LibraryService.LibraryRootFolder != null && agentID == m_LibraryService.LibraryRootFolder.Owner) |
||
225 | { |
||
226 | if ((fold = m_LibraryService.LibraryRootFolder.FindFolder(folderID)) != null) |
||
227 | { |
||
228 | InventoryCollection ret = new InventoryCollection(); |
||
229 | ret.Folders = new List<InventoryFolderBase>(); |
||
230 | ret.Items = fold.RequestListOfItems(); |
||
231 | descendents = ret.Folders.Count + ret.Items.Count; |
||
232 | |||
233 | return ret; |
||
234 | } |
||
235 | } |
||
236 | |||
237 | InventoryCollection contents = new InventoryCollection(); |
||
238 | |||
239 | if (folderID != UUID.Zero) |
||
240 | { |
||
241 | contents = m_InventoryService.GetFolderContent(agentID, folderID); |
||
242 | InventoryFolderBase containingFolder = new InventoryFolderBase(); |
||
243 | containingFolder.ID = folderID; |
||
244 | containingFolder.Owner = agentID; |
||
245 | containingFolder = m_InventoryService.GetFolder(containingFolder); |
||
246 | |||
247 | if (containingFolder != null) |
||
248 | { |
||
249 | // m_log.DebugFormat( |
||
250 | // "[WEB FETCH INV DESC HANDLER]: Retrieved folder {0} {1} for agent id {2}", |
||
251 | // containingFolder.Name, containingFolder.ID, agentID); |
||
252 | |||
253 | version = containingFolder.Version; |
||
254 | |||
255 | if (fetchItems) |
||
256 | { |
||
257 | List<InventoryItemBase> itemsToReturn = contents.Items; |
||
258 | List<InventoryItemBase> originalItems = new List<InventoryItemBase>(itemsToReturn); |
||
259 | |||
260 | // descendents must only include the links, not the linked items we add |
||
261 | descendents = originalItems.Count; |
||
262 | |||
263 | // Add target items for links in this folder before the links themselves. |
||
264 | foreach (InventoryItemBase item in originalItems) |
||
265 | { |
||
266 | if (item.AssetType == (int)AssetType.Link) |
||
267 | { |
||
268 | InventoryItemBase linkedItem = m_InventoryService.GetItem(new InventoryItemBase(item.AssetID)); |
||
269 | |||
270 | // Take care of genuinely broken links where the target doesn't exist |
||
271 | // HACK: Also, don't follow up links that just point to other links. In theory this is legitimate, |
||
272 | // but no viewer has been observed to set these up and this is the lazy way of avoiding cycles |
||
273 | // rather than having to keep track of every folder requested in the recursion. |
||
274 | if (linkedItem != null && linkedItem.AssetType != (int)AssetType.Link) |
||
275 | itemsToReturn.Insert(0, linkedItem); |
||
276 | } |
||
277 | } |
||
278 | |||
279 | // Now scan for folder links and insert the items they target and those links at the head of the return data |
||
280 | foreach (InventoryItemBase item in originalItems) |
||
281 | { |
||
282 | if (item.AssetType == (int)AssetType.LinkFolder) |
||
283 | { |
||
284 | InventoryCollection linkedFolderContents = m_InventoryService.GetFolderContent(ownerID, item.AssetID); |
||
285 | List<InventoryItemBase> links = linkedFolderContents.Items; |
||
286 | |||
287 | itemsToReturn.InsertRange(0, links); |
||
288 | |||
289 | foreach (InventoryItemBase link in linkedFolderContents.Items) |
||
290 | { |
||
291 | // Take care of genuinely broken links where the target doesn't exist |
||
292 | // HACK: Also, don't follow up links that just point to other links. In theory this is legitimate, |
||
293 | // but no viewer has been observed to set these up and this is the lazy way of avoiding cycles |
||
294 | // rather than having to keep track of every folder requested in the recursion. |
||
295 | if (link != null) |
||
296 | { |
||
297 | // m_log.DebugFormat( |
||
298 | // "[WEB FETCH INV DESC HANDLER]: Adding item {0} {1} from folder {2} linked from {3}", |
||
299 | // link.Name, (AssetType)link.AssetType, item.AssetID, containingFolder.Name); |
||
300 | |||
301 | InventoryItemBase linkedItem |
||
302 | = m_InventoryService.GetItem(new InventoryItemBase(link.AssetID)); |
||
303 | |||
304 | if (linkedItem != null) |
||
305 | itemsToReturn.Insert(0, linkedItem); |
||
306 | } |
||
307 | } |
||
308 | } |
||
309 | } |
||
310 | } |
||
311 | |||
312 | // foreach (InventoryItemBase item in contents.Items) |
||
313 | // { |
||
314 | // m_log.DebugFormat( |
||
315 | // "[WEB FETCH INV DESC HANDLER]: Returning item {0}, type {1}, parent {2} in {3} {4}", |
||
316 | // item.Name, (AssetType)item.AssetType, item.Folder, containingFolder.Name, containingFolder.ID); |
||
317 | // } |
||
318 | |||
319 | // ===== |
||
320 | |||
321 | // |
||
322 | // foreach (InventoryItemBase linkedItem in linkedItemsToAdd) |
||
323 | // { |
||
324 | // m_log.DebugFormat( |
||
325 | // "[WEB FETCH INV DESC HANDLER]: Inserted linked item {0} for link in folder {1} for agent {2}", |
||
326 | // linkedItem.Name, folderID, agentID); |
||
327 | // |
||
328 | // contents.Items.Add(linkedItem); |
||
329 | // } |
||
330 | // |
||
331 | // // If the folder requested contains links, then we need to send those folders first, otherwise the links |
||
332 | // // will be broken in the viewer. |
||
333 | // HashSet<UUID> linkedItemFolderIdsToSend = new HashSet<UUID>(); |
||
334 | // foreach (InventoryItemBase item in contents.Items) |
||
335 | // { |
||
336 | // if (item.AssetType == (int)AssetType.Link) |
||
337 | // { |
||
338 | // InventoryItemBase linkedItem = m_InventoryService.GetItem(new InventoryItemBase(item.AssetID)); |
||
339 | // |
||
340 | // // Take care of genuinely broken links where the target doesn't exist |
||
341 | // // HACK: Also, don't follow up links that just point to other links. In theory this is legitimate, |
||
342 | // // but no viewer has been observed to set these up and this is the lazy way of avoiding cycles |
||
343 | // // rather than having to keep track of every folder requested in the recursion. |
||
344 | // if (linkedItem != null && linkedItem.AssetType != (int)AssetType.Link) |
||
345 | // { |
||
346 | // // We don't need to send the folder if source and destination of the link are in the same |
||
347 | // // folder. |
||
348 | // if (linkedItem.Folder != containingFolder.ID) |
||
349 | // linkedItemFolderIdsToSend.Add(linkedItem.Folder); |
||
350 | // } |
||
351 | // } |
||
352 | // } |
||
353 | // |
||
354 | // foreach (UUID linkedItemFolderId in linkedItemFolderIdsToSend) |
||
355 | // { |
||
356 | // m_log.DebugFormat( |
||
357 | // "[WEB FETCH INV DESC HANDLER]: Recursively fetching folder {0} linked by item in folder {1} for agent {2}", |
||
358 | // linkedItemFolderId, folderID, agentID); |
||
359 | // |
||
360 | // int dummyVersion; |
||
361 | // InventoryCollection linkedCollection |
||
362 | // = Fetch( |
||
363 | // agentID, linkedItemFolderId, ownerID, fetchFolders, fetchItems, sortOrder, out dummyVersion); |
||
364 | // |
||
365 | // InventoryFolderBase linkedFolder = new InventoryFolderBase(linkedItemFolderId); |
||
366 | // linkedFolder.Owner = agentID; |
||
367 | // linkedFolder = m_InventoryService.GetFolder(linkedFolder); |
||
368 | // |
||
369 | //// contents.Folders.AddRange(linkedCollection.Folders); |
||
370 | // |
||
371 | // contents.Folders.Add(linkedFolder); |
||
372 | // contents.Items.AddRange(linkedCollection.Items); |
||
373 | // } |
||
374 | // } |
||
375 | } |
||
376 | } |
||
377 | else |
||
378 | { |
||
379 | // Lost items don't really need a version |
||
380 | version = 1; |
||
381 | } |
||
382 | |||
383 | return contents; |
||
384 | |||
385 | } |
||
386 | /// <summary> |
||
387 | /// Convert an internal inventory folder object into an LLSD object. |
||
388 | /// </summary> |
||
389 | /// <param name="invFolder"></param> |
||
390 | /// <returns></returns> |
||
391 | private LLSDInventoryFolder ConvertInventoryFolder(InventoryFolderBase invFolder) |
||
392 | { |
||
393 | LLSDInventoryFolder llsdFolder = new LLSDInventoryFolder(); |
||
394 | llsdFolder.folder_id = invFolder.ID; |
||
395 | llsdFolder.parent_id = invFolder.ParentID; |
||
396 | llsdFolder.name = invFolder.Name; |
||
397 | llsdFolder.type = invFolder.Type; |
||
398 | llsdFolder.preferred_type = -1; |
||
399 | |||
400 | return llsdFolder; |
||
401 | } |
||
402 | |||
403 | /// <summary> |
||
404 | /// Convert an internal inventory item object into an LLSD object. |
||
405 | /// </summary> |
||
406 | /// <param name="invItem"></param> |
||
407 | /// <returns></returns> |
||
408 | private LLSDInventoryItem ConvertInventoryItem(InventoryItemBase invItem) |
||
409 | { |
||
410 | LLSDInventoryItem llsdItem = new LLSDInventoryItem(); |
||
411 | llsdItem.asset_id = invItem.AssetID; |
||
412 | llsdItem.created_at = invItem.CreationDate; |
||
413 | llsdItem.desc = invItem.Description; |
||
414 | llsdItem.flags = (int)invItem.Flags; |
||
415 | llsdItem.item_id = invItem.ID; |
||
416 | llsdItem.name = invItem.Name; |
||
417 | llsdItem.parent_id = invItem.Folder; |
||
418 | llsdItem.type = invItem.AssetType; |
||
419 | llsdItem.inv_type = invItem.InvType; |
||
420 | |||
421 | llsdItem.permissions = new LLSDPermissions(); |
||
422 | llsdItem.permissions.creator_id = invItem.CreatorIdAsUuid; |
||
423 | llsdItem.permissions.base_mask = (int)invItem.CurrentPermissions; |
||
424 | llsdItem.permissions.everyone_mask = (int)invItem.EveryOnePermissions; |
||
425 | llsdItem.permissions.group_id = invItem.GroupID; |
||
426 | llsdItem.permissions.group_mask = (int)invItem.GroupPermissions; |
||
427 | llsdItem.permissions.is_owner_group = invItem.GroupOwned; |
||
428 | llsdItem.permissions.next_owner_mask = (int)invItem.NextPermissions; |
||
429 | llsdItem.permissions.owner_id = invItem.Owner; |
||
430 | llsdItem.permissions.owner_mask = (int)invItem.CurrentPermissions; |
||
431 | llsdItem.sale_info = new LLSDSaleInfo(); |
||
432 | llsdItem.sale_info.sale_price = invItem.SalePrice; |
||
433 | llsdItem.sale_info.sale_type = invItem.SaleType; |
||
434 | |||
435 | return llsdItem; |
||
436 | } |
||
437 | } |
||
438 | } |