clockwerk-opensim-stable – Blame information for rev 1

Subversion Repositories:
Rev:
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.Generic;
30 using System.Threading;
31 using OpenMetaverse;
32 using OpenMetaverse.Packets;
33 using OpenSim.Framework;
34 using OpenSim.Framework.Communications;
35 using OpenSim.Services.Interfaces;
36  
37 namespace OpenSim.Region.Framework.Scenes
38 {
39 public partial class Scene
40 {
41 /// <summary>
42 /// Send chat to listeners.
43 /// </summary>
44 /// <param name='message'></param>
45 /// <param name='type'>/param>
46 /// <param name='channel'></param>
47 /// <param name='fromPos'></param>
48 /// <param name='fromName'></param>
49 /// <param name='fromID'></param>
50 /// <param name='targetID'></param>
51 /// <param name='fromAgent'></param>
52 /// <param name='broadcast'></param>
53 protected void SimChat(byte[] message, ChatTypeEnum type, int channel, Vector3 fromPos, string fromName,
54 UUID fromID, UUID targetID, bool fromAgent, bool broadcast)
55 {
56 OSChatMessage args = new OSChatMessage();
57  
58 args.Message = Utils.BytesToString(message);
59 args.Channel = channel;
60 args.Type = type;
61 args.Position = fromPos;
62 args.SenderUUID = fromID;
63 args.Scene = this;
64  
65 if (fromAgent)
66 {
67 ScenePresence user = GetScenePresence(fromID);
68 if (user != null)
69 args.Sender = user.ControllingClient;
70 }
71 else
72 {
73 SceneObjectPart obj = GetSceneObjectPart(fromID);
74 args.SenderObject = obj;
75 }
76  
77 args.From = fromName;
78 args.TargetUUID = targetID;
79  
80 // m_log.DebugFormat(
81 // "[SCENE]: Sending message {0} on channel {1}, type {2} from {3}, broadcast {4}",
82 // args.Message.Replace("\n", "\\n"), args.Channel, args.Type, fromName, broadcast);
83  
84 if (broadcast)
85 EventManager.TriggerOnChatBroadcast(this, args);
86 else
87 EventManager.TriggerOnChatFromWorld(this, args);
88 }
89  
90 protected void SimChat(byte[] message, ChatTypeEnum type, int channel, Vector3 fromPos, string fromName,
91 UUID fromID, bool fromAgent, bool broadcast)
92 {
93 SimChat(message, type, channel, fromPos, fromName, fromID, UUID.Zero, fromAgent, broadcast);
94 }
95  
96 /// <summary>
97 ///
98 /// </summary>
99 /// <param name="message"></param>
100 /// <param name="type"></param>
101 /// <param name="fromPos"></param>
102 /// <param name="fromName"></param>
103 /// <param name="fromAgentID"></param>
104 public void SimChat(byte[] message, ChatTypeEnum type, int channel, Vector3 fromPos, string fromName,
105 UUID fromID, bool fromAgent)
106 {
107 SimChat(message, type, channel, fromPos, fromName, fromID, fromAgent, false);
108 }
109  
110 public void SimChat(string message, ChatTypeEnum type, Vector3 fromPos, string fromName, UUID fromID, bool fromAgent)
111 {
112 SimChat(Utils.StringToBytes(message), type, 0, fromPos, fromName, fromID, fromAgent);
113 }
114  
115 public void SimChat(string message, string fromName)
116 {
117 SimChat(message, ChatTypeEnum.Broadcast, Vector3.Zero, fromName, UUID.Zero, false);
118 }
119  
120 /// <summary>
121 ///
122 /// </summary>
123 /// <param name="message"></param>
124 /// <param name="type"></param>
125 /// <param name="fromPos"></param>
126 /// <param name="fromName"></param>
127 /// <param name="fromAgentID"></param>
128 public void SimChatBroadcast(byte[] message, ChatTypeEnum type, int channel, Vector3 fromPos, string fromName,
129 UUID fromID, bool fromAgent)
130 {
131 SimChat(message, type, channel, fromPos, fromName, fromID, fromAgent, true);
132 }
133 /// <summary>
134 ///
135 /// </summary>
136 /// <param name="message"></param>
137 /// <param name="type"></param>
138 /// <param name="fromPos"></param>
139 /// <param name="fromName"></param>
140 /// <param name="fromAgentID"></param>
141 /// <param name="targetID"></param>
142 public void SimChatToAgent(UUID targetID, byte[] message, Vector3 fromPos, string fromName, UUID fromID, bool fromAgent)
143 {
144 SimChat(message, ChatTypeEnum.Say, 0, fromPos, fromName, fromID, targetID, fromAgent, false);
145 }
146  
147 /// <summary>
148 /// Invoked when the client requests a prim.
149 /// </summary>
150 /// <param name="primLocalID"></param>
151 /// <param name="remoteClient"></param>
152 public void RequestPrim(uint primLocalID, IClientAPI remoteClient)
153 {
154 SceneObjectGroup sog = GetGroupByPrim(primLocalID);
155  
156 if (sog != null)
157 sog.SendFullUpdateToClient(remoteClient);
158 }
159  
160 /// <summary>
161 /// Invoked when the client selects a prim.
162 /// </summary>
163 /// <param name="primLocalID"></param>
164 /// <param name="remoteClient"></param>
165 public void SelectPrim(uint primLocalID, IClientAPI remoteClient)
166 {
167 SceneObjectPart part = GetSceneObjectPart(primLocalID);
168  
169 if (null == part)
170 return;
171  
172 if (part.IsRoot)
173 {
174 SceneObjectGroup sog = part.ParentGroup;
175 sog.SendPropertiesToClient(remoteClient);
176 sog.IsSelected = true;
177  
178 // A prim is only tainted if it's allowed to be edited by the person clicking it.
179 if (Permissions.CanEditObject(sog.UUID, remoteClient.AgentId)
180 || Permissions.CanMoveObject(sog.UUID, remoteClient.AgentId))
181 {
182 EventManager.TriggerParcelPrimCountTainted();
183 }
184 }
185 else
186 {
187 part.SendPropertiesToClient(remoteClient);
188 }
189 }
190  
191 /// <summary>
192 /// Handle the update of an object's user group.
193 /// </summary>
194 /// <param name="remoteClient"></param>
195 /// <param name="groupID"></param>
196 /// <param name="objectLocalID"></param>
197 /// <param name="Garbage"></param>
198 private void HandleObjectGroupUpdate(
199 IClientAPI remoteClient, UUID groupID, uint objectLocalID, UUID Garbage)
200 {
201 if (m_groupsModule == null)
202 return;
203  
204 // XXX: Might be better to get rid of this special casing and have GetMembershipData return something
205 // reasonable for a UUID.Zero group.
206 if (groupID != UUID.Zero)
207 {
208 GroupMembershipData gmd = m_groupsModule.GetMembershipData(groupID, remoteClient.AgentId);
209  
210 if (gmd == null)
211 {
212 // m_log.WarnFormat(
213 // "[GROUPS]: User {0} is not a member of group {1} so they can't update {2} to this group",
214 // remoteClient.Name, GroupID, objectLocalID);
215  
216 return;
217 }
218 }
219  
220 SceneObjectGroup so = ((Scene)remoteClient.Scene).GetGroupByPrim(objectLocalID);
221 if (so != null)
222 {
223 if (so.OwnerID == remoteClient.AgentId)
224 {
225 so.SetGroup(groupID, remoteClient);
226 }
227 }
228 }
229  
230 /// <summary>
231 /// Handle the deselection of a prim from the client.
232 /// </summary>
233 /// <param name="primLocalID"></param>
234 /// <param name="remoteClient"></param>
235 public void DeselectPrim(uint primLocalID, IClientAPI remoteClient)
236 {
237 SceneObjectPart part = GetSceneObjectPart(primLocalID);
238 if (part == null)
239 return;
240  
241 // A deselect packet contains all the local prims being deselected. However, since selection is still
242 // group based we only want the root prim to trigger a full update - otherwise on objects with many prims
243 // we end up sending many duplicate ObjectUpdates
244 if (part.ParentGroup.RootPart.LocalId != part.LocalId)
245 return;
246  
247 bool isAttachment = false;
248  
249 // This is wrong, wrong, wrong. Selection should not be
250 // handled by group, but by prim. Legacy cruft.
251 // TODO: Make selection flagging per prim!
252 //
253 part.ParentGroup.IsSelected = false;
254  
255 if (part.ParentGroup.IsAttachment)
256 isAttachment = true;
257 else
258 part.ParentGroup.ScheduleGroupForFullUpdate();
259  
260 // If it's not an attachment, and we are allowed to move it,
261 // then we might have done so. If we moved across a parcel
262 // boundary, we will need to recount prims on the parcels.
263 // For attachments, that makes no sense.
264 //
265 if (!isAttachment)
266 {
267 if (Permissions.CanEditObject(
268 part.UUID, remoteClient.AgentId)
269 || Permissions.CanMoveObject(
270 part.UUID, remoteClient.AgentId))
271 EventManager.TriggerParcelPrimCountTainted();
272 }
273 }
274  
275 public virtual void ProcessMoneyTransferRequest(UUID source, UUID destination, int amount,
276 int transactiontype, string description)
277 {
278 EventManager.MoneyTransferArgs args = new EventManager.MoneyTransferArgs(source, destination, amount,
279 transactiontype, description);
280  
281 EventManager.TriggerMoneyTransfer(this, args);
282 }
283  
284 public virtual void ProcessParcelBuy(UUID agentId, UUID groupId, bool final, bool groupOwned,
285 bool removeContribution, int parcelLocalID, int parcelArea, int parcelPrice, bool authenticated)
286 {
287 EventManager.LandBuyArgs args = new EventManager.LandBuyArgs(agentId, groupId, final, groupOwned,
288 removeContribution, parcelLocalID, parcelArea,
289 parcelPrice, authenticated);
290  
291 // First, allow all validators a stab at it
292 m_eventManager.TriggerValidateLandBuy(this, args);
293  
294 // Then, check validation and transfer
295 m_eventManager.TriggerLandBuy(this, args);
296 }
297  
298 public virtual void ProcessObjectGrab(uint localID, Vector3 offsetPos, IClientAPI remoteClient, List<SurfaceTouchEventArgs> surfaceArgs)
299 {
300 SceneObjectPart part = GetSceneObjectPart(localID);
301  
302 if (part == null)
303 return;
304  
305 SceneObjectGroup obj = part.ParentGroup;
306  
307 SurfaceTouchEventArgs surfaceArg = null;
308 if (surfaceArgs != null && surfaceArgs.Count > 0)
309 surfaceArg = surfaceArgs[0];
310  
311 // Currently only grab/touch for the single prim
312 // the client handles rez correctly
313 obj.ObjectGrabHandler(localID, offsetPos, remoteClient);
314  
315 // If the touched prim handles touches, deliver it
316 // If not, deliver to root prim
317 if ((part.ScriptEvents & scriptEvents.touch_start) != 0)
318 EventManager.TriggerObjectGrab(part.LocalId, 0, part.OffsetPosition, remoteClient, surfaceArg);
319  
320 // Deliver to the root prim if the touched prim doesn't handle touches
321 // or if we're meant to pass on touches anyway. Don't send to root prim
322 // if prim touched is the root prim as we just did it
323 if (((part.ScriptEvents & scriptEvents.touch_start) == 0) ||
324 (part.PassTouches && (part.LocalId != obj.RootPart.LocalId)))
325 {
326 EventManager.TriggerObjectGrab(obj.RootPart.LocalId, part.LocalId, part.OffsetPosition, remoteClient, surfaceArg);
327 }
328 }
329  
330 public virtual void ProcessObjectGrabUpdate(
331 UUID objectID, Vector3 offset, Vector3 pos, IClientAPI remoteClient, List<SurfaceTouchEventArgs> surfaceArgs)
332 {
333 SceneObjectPart part = GetSceneObjectPart(objectID);
334 if (part == null)
335 return;
336  
337 SceneObjectGroup obj = part.ParentGroup;
338  
339 SurfaceTouchEventArgs surfaceArg = null;
340 if (surfaceArgs != null && surfaceArgs.Count > 0)
341 surfaceArg = surfaceArgs[0];
342  
343 // If the touched prim handles touches, deliver it
344 // If not, deliver to root prim
345 if ((part.ScriptEvents & scriptEvents.touch) != 0)
346 EventManager.TriggerObjectGrabbing(part.LocalId, 0, part.OffsetPosition, remoteClient, surfaceArg);
347 // Deliver to the root prim if the touched prim doesn't handle touches
348 // or if we're meant to pass on touches anyway. Don't send to root prim
349 // if prim touched is the root prim as we just did it
350 if (((part.ScriptEvents & scriptEvents.touch) == 0) ||
351 (part.PassTouches && (part.LocalId != obj.RootPart.LocalId)))
352 {
353 EventManager.TriggerObjectGrabbing(obj.RootPart.LocalId, part.LocalId, part.OffsetPosition, remoteClient, surfaceArg);
354 }
355 }
356  
357 public virtual void ProcessObjectDeGrab(uint localID, IClientAPI remoteClient, List<SurfaceTouchEventArgs> surfaceArgs)
358 {
359 SceneObjectPart part = GetSceneObjectPart(localID);
360 if (part == null)
361 return;
362  
363 SceneObjectGroup obj = part.ParentGroup;
364  
365 SurfaceTouchEventArgs surfaceArg = null;
366 if (surfaceArgs != null && surfaceArgs.Count > 0)
367 surfaceArg = surfaceArgs[0];
368  
369 // If the touched prim handles touches, deliver it
370 // If not, deliver to root prim
371 if ((part.ScriptEvents & scriptEvents.touch_end) != 0)
372 EventManager.TriggerObjectDeGrab(part.LocalId, 0, remoteClient, surfaceArg);
373 else
374 EventManager.TriggerObjectDeGrab(obj.RootPart.LocalId, part.LocalId, remoteClient, surfaceArg);
375 }
376  
377 public void ProcessScriptReset(IClientAPI remoteClient, UUID objectID,
378 UUID itemID)
379 {
380 SceneObjectPart part=GetSceneObjectPart(objectID);
381 if (part == null)
382 return;
383  
384 if (Permissions.CanResetScript(objectID, itemID, remoteClient.AgentId))
385 {
386 EventManager.TriggerScriptReset(part.LocalId, itemID);
387 }
388 }
389  
390 void ProcessViewerEffect(IClientAPI remoteClient, List<ViewerEffectEventHandlerArg> args)
391 {
392 // TODO: don't create new blocks if recycling an old packet
393 bool discardableEffects = true;
394 ViewerEffectPacket.EffectBlock[] effectBlockArray = new ViewerEffectPacket.EffectBlock[args.Count];
395 for (int i = 0; i < args.Count; i++)
396 {
397 ViewerEffectPacket.EffectBlock effect = new ViewerEffectPacket.EffectBlock();
398 effect.AgentID = args[i].AgentID;
399 effect.Color = args[i].Color;
400 effect.Duration = args[i].Duration;
401 effect.ID = args[i].ID;
402 effect.Type = args[i].Type;
403 effect.TypeData = args[i].TypeData;
404 effectBlockArray[i] = effect;
405  
406 if ((EffectType)effect.Type != EffectType.LookAt && (EffectType)effect.Type != EffectType.Beam)
407 discardableEffects = false;
408  
409 //m_log.DebugFormat("[YYY]: VE {0} {1} {2}", effect.AgentID, effect.Duration, (EffectType)effect.Type);
410 }
411  
412 ForEachScenePresence(sp =>
413 {
414 if (sp.ControllingClient.AgentId != remoteClient.AgentId)
415 {
416 if (!discardableEffects ||
417 (discardableEffects && ShouldSendDiscardableEffect(remoteClient, sp)))
418 {
419 //m_log.DebugFormat("[YYY]: Sending to {0}", sp.UUID);
420 sp.ControllingClient.SendViewerEffect(effectBlockArray);
421 }
422 //else
423 // m_log.DebugFormat("[YYY]: Not sending to {0}", sp.UUID);
424 }
425 });
426 }
427  
428 private bool ShouldSendDiscardableEffect(IClientAPI thisClient, ScenePresence other)
429 {
430 return Vector3.Distance(other.CameraPosition, thisClient.SceneAgent.AbsolutePosition) < 10;
431 }
432  
433 /// <summary>
434 /// Tell the client about the various child items and folders contained in the requested folder.
435 /// </summary>
436 /// <param name="remoteClient"></param>
437 /// <param name="folderID"></param>
438 /// <param name="ownerID"></param>
439 /// <param name="fetchFolders"></param>
440 /// <param name="fetchItems"></param>
441 /// <param name="sortOrder"></param>
442 public void HandleFetchInventoryDescendents(IClientAPI remoteClient, UUID folderID, UUID ownerID,
443 bool fetchFolders, bool fetchItems, int sortOrder)
444 {
445 // m_log.DebugFormat(
446 // "[USER INVENTORY]: HandleFetchInventoryDescendents() for {0}, folder={1}, fetchFolders={2}, fetchItems={3}, sortOrder={4}",
447 // remoteClient.Name, folderID, fetchFolders, fetchItems, sortOrder);
448  
449 if (folderID == UUID.Zero)
450 return;
451  
452 // FIXME MAYBE: We're not handling sortOrder!
453  
454 // TODO: This code for looking in the folder for the library should be folded somewhere else
455 // so that this class doesn't have to know the details (and so that multiple libraries, etc.
456 // can be handled transparently).
457 InventoryFolderImpl fold = null;
458 if (LibraryService != null && LibraryService.LibraryRootFolder != null)
459 {
460 if ((fold = LibraryService.LibraryRootFolder.FindFolder(folderID)) != null)
461 {
462 remoteClient.SendInventoryFolderDetails(
463 fold.Owner, folderID, fold.RequestListOfItems(),
464 fold.RequestListOfFolders(), fold.Version, fetchFolders, fetchItems);
465 return;
466 }
467 }
468  
469 // We're going to send the reply async, because there may be
470 // an enormous quantity of packets -- basically the entire inventory!
471 // We don't want to block the client thread while all that is happening.
472 SendInventoryDelegate d = SendInventoryAsync;
473 d.BeginInvoke(remoteClient, folderID, ownerID, fetchFolders, fetchItems, sortOrder, SendInventoryComplete, d);
474 }
475  
476 delegate void SendInventoryDelegate(IClientAPI remoteClient, UUID folderID, UUID ownerID, bool fetchFolders, bool fetchItems, int sortOrder);
477  
478 void SendInventoryAsync(IClientAPI remoteClient, UUID folderID, UUID ownerID, bool fetchFolders, bool fetchItems, int sortOrder)
479 {
480 SendInventoryUpdate(remoteClient, new InventoryFolderBase(folderID), fetchFolders, fetchItems);
481 }
482  
483 void SendInventoryComplete(IAsyncResult iar)
484 {
485 SendInventoryDelegate d = (SendInventoryDelegate)iar.AsyncState;
486 d.EndInvoke(iar);
487 }
488  
489 /// <summary>
490 /// Handle an inventory folder creation request from the client.
491 /// </summary>
492 /// <param name="remoteClient"></param>
493 /// <param name="folderID"></param>
494 /// <param name="folderType"></param>
495 /// <param name="folderName"></param>
496 /// <param name="parentID"></param>
497 public void HandleCreateInventoryFolder(IClientAPI remoteClient, UUID folderID, ushort folderType,
498 string folderName, UUID parentID)
499 {
500 InventoryFolderBase folder = new InventoryFolderBase(folderID, folderName, remoteClient.AgentId, (short)folderType, parentID, 1);
501 if (!InventoryService.AddFolder(folder))
502 {
503 m_log.WarnFormat(
504 "[AGENT INVENTORY]: Failed to create folder for user {0} {1}",
505 remoteClient.Name, remoteClient.AgentId);
506 }
507 }
508  
509 /// <summary>
510 /// Handle a client request to update the inventory folder
511 /// </summary>
512 ///
513 /// FIXME: We call add new inventory folder because in the data layer, we happen to use an SQL REPLACE
514 /// so this will work to rename an existing folder. Needless to say, to rely on this is very confusing,
515 /// and needs to be changed.
516 ///
517 /// <param name="remoteClient"></param>
518 /// <param name="folderID"></param>
519 /// <param name="type"></param>
520 /// <param name="name"></param>
521 /// <param name="parentID"></param>
522 public void HandleUpdateInventoryFolder(IClientAPI remoteClient, UUID folderID, ushort type, string name,
523 UUID parentID)
524 {
525 // m_log.DebugFormat(
526 // "[AGENT INVENTORY]: Updating inventory folder {0} {1} for {2} {3}", folderID, name, remoteClient.Name, remoteClient.AgentId);
527  
528 InventoryFolderBase folder = new InventoryFolderBase(folderID, remoteClient.AgentId);
529 folder = InventoryService.GetFolder(folder);
530 if (folder != null)
531 {
532 folder.Name = name;
533 folder.Type = (short)type;
534 folder.ParentID = parentID;
535 if (!InventoryService.UpdateFolder(folder))
536 {
537 m_log.ErrorFormat(
538 "[AGENT INVENTORY]: Failed to update folder for user {0} {1}",
539 remoteClient.Name, remoteClient.AgentId);
540 }
541 }
542 }
543  
544 public void HandleMoveInventoryFolder(IClientAPI remoteClient, UUID folderID, UUID parentID)
545 {
546 InventoryFolderBase folder = new InventoryFolderBase(folderID, remoteClient.AgentId);
547 folder = InventoryService.GetFolder(folder);
548 if (folder != null)
549 {
550 folder.ParentID = parentID;
551 if (!InventoryService.MoveFolder(folder))
552 m_log.WarnFormat("[AGENT INVENTORY]: could not move folder {0}", folderID);
553 else
554 m_log.DebugFormat("[AGENT INVENTORY]: folder {0} moved to parent {1}", folderID, parentID);
555 }
556 else
557 {
558 m_log.WarnFormat("[AGENT INVENTORY]: request to move folder {0} but folder not found", folderID);
559 }
560 }
561  
562 delegate void PurgeFolderDelegate(UUID userID, UUID folder);
563  
564 /// <summary>
565 /// This should delete all the items and folders in the given directory.
566 /// </summary>
567 /// <param name="remoteClient"></param>
568 /// <param name="folderID"></param>
569 public void HandlePurgeInventoryDescendents(IClientAPI remoteClient, UUID folderID)
570 {
571 PurgeFolderDelegate d = PurgeFolderAsync;
572 try
573 {
574 d.BeginInvoke(remoteClient.AgentId, folderID, PurgeFolderCompleted, d);
575 }
576 catch (Exception e)
577 {
578 m_log.WarnFormat("[AGENT INVENTORY]: Exception on purge folder for user {0}: {1}", remoteClient.AgentId, e.Message);
579 }
580 }
581  
582 private void PurgeFolderAsync(UUID userID, UUID folderID)
583 {
584 InventoryFolderBase folder = new InventoryFolderBase(folderID, userID);
585  
586 if (InventoryService.PurgeFolder(folder))
587 m_log.DebugFormat("[AGENT INVENTORY]: folder {0} purged successfully", folderID);
588 else
589 m_log.WarnFormat("[AGENT INVENTORY]: could not purge folder {0}", folderID);
590 }
591  
592 private void PurgeFolderCompleted(IAsyncResult iar)
593 {
594 PurgeFolderDelegate d = (PurgeFolderDelegate)iar.AsyncState;
595 d.EndInvoke(iar);
596 }
597 }
598 }