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