OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
InventoryTransferModule.cs
Go to the documentation of this file.
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.Reflection;
31 using log4net;
32 using Mono.Addins;
33 using Nini.Config;
34 using OpenMetaverse;
35 using OpenSim.Framework;
36 using OpenSim.Region.Framework.Interfaces;
37 using OpenSim.Region.Framework.Scenes;
38 using OpenSim.Services.Interfaces;
39 
40 namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
41 {
42  [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "InventoryTransferModule")]
44  {
45  private static readonly ILog m_log
46  = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
47 
49  private List<Scene> m_Scenelist = new List<Scene>();
50 
51  private IMessageTransferModule m_TransferModule;
52  private bool m_Enabled = true;
53 
54  #region Region Module interface
55 
56  public void Initialise(IConfigSource config)
57  {
58  if (config.Configs["Messaging"] != null)
59  {
60  // Allow disabling this module in config
61  //
62  if (config.Configs["Messaging"].GetString(
63  "InventoryTransferModule", "InventoryTransferModule") !=
64  "InventoryTransferModule")
65  {
66  m_Enabled = false;
67  return;
68  }
69  }
70  }
71 
72  public void AddRegion(Scene scene)
73  {
74  if (!m_Enabled)
75  return;
76 
77  m_Scenelist.Add(scene);
78 
79 // scene.RegisterModuleInterface<IInventoryTransferModule>(this);
80 
81  scene.EventManager.OnNewClient += OnNewClient;
82  scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage;
83  }
84 
85  public void RegionLoaded(Scene scene)
86  {
87  if (m_TransferModule == null)
88  {
89  m_TransferModule = m_Scenelist[0].RequestModuleInterface<IMessageTransferModule>();
90  if (m_TransferModule == null)
91  {
92  m_log.Error("[INVENTORY TRANSFER]: No Message transfer module found, transfers will be local only");
93  m_Enabled = false;
94 
95 // m_Scenelist.Clear();
96 // scene.EventManager.OnNewClient -= OnNewClient;
97  scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage;
98  }
99  }
100  }
101 
102  public void RemoveRegion(Scene scene)
103  {
104  scene.EventManager.OnNewClient -= OnNewClient;
105  scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage;
106  m_Scenelist.Remove(scene);
107  }
108 
109  public void PostInitialise()
110  {
111  }
112 
113  public void Close()
114  {
115  }
116 
117  public string Name
118  {
119  get { return "InventoryModule"; }
120  }
121 
122  public Type ReplaceableInterface
123  {
124  get { return null; }
125  }
126 
127  #endregion
128 
129  private void OnNewClient(IClientAPI client)
130  {
131  // Inventory giving is conducted via instant message
132  client.OnInstantMessage += OnInstantMessage;
133  }
134 
135  private Scene FindClientScene(UUID agentId)
136  {
137  lock (m_Scenelist)
138  {
139  foreach (Scene scene in m_Scenelist)
140  {
141  ScenePresence presence = scene.GetScenePresence(agentId);
142  if (presence != null)
143  return scene;
144  }
145  }
146  return null;
147  }
148 
149  private void OnInstantMessage(IClientAPI client, GridInstantMessage im)
150  {
151 // m_log.DebugFormat(
152 // "[INVENTORY TRANSFER]: {0} IM type received from client {1}. From={2} ({3}), To={4}",
153 // (InstantMessageDialog)im.dialog, client.Name,
154 // im.fromAgentID, im.fromAgentName, im.toAgentID);
155 
156  Scene scene = FindClientScene(client.AgentId);
157 
158  if (scene == null) // Something seriously wrong here.
159  return;
160 
161  if (im.dialog == (byte) InstantMessageDialog.InventoryOffered)
162  {
163  //m_log.DebugFormat("Asset type {0}", ((AssetType)im.binaryBucket[0]));
164 
165  if (im.binaryBucket.Length < 17) // Invalid
166  return;
167 
168  UUID recipientID = new UUID(im.toAgentID);
169  ScenePresence user = scene.GetScenePresence(recipientID);
170  UUID copyID;
171 
172  // First byte is the asset type
173  AssetType assetType = (AssetType)im.binaryBucket[0];
174 
175  if (AssetType.Folder == assetType)
176  {
177  UUID folderID = new UUID(im.binaryBucket, 1);
178 
179  m_log.DebugFormat(
180  "[INVENTORY TRANSFER]: Inserting original folder {0} into agent {1}'s inventory",
181  folderID, new UUID(im.toAgentID));
182 
183  InventoryFolderBase folderCopy
184  = scene.GiveInventoryFolder(client, recipientID, client.AgentId, folderID, UUID.Zero);
185 
186  if (folderCopy == null)
187  {
188  client.SendAgentAlertMessage("Can't find folder to give. Nothing given.", false);
189  return;
190  }
191 
192  // The outgoing binary bucket should contain only the byte which signals an asset folder is
193  // being copied and the following bytes for the copied folder's UUID
194  copyID = folderCopy.ID;
195  byte[] copyIDBytes = copyID.GetBytes();
196  im.binaryBucket = new byte[1 + copyIDBytes.Length];
197  im.binaryBucket[0] = (byte)AssetType.Folder;
198  Array.Copy(copyIDBytes, 0, im.binaryBucket, 1, copyIDBytes.Length);
199 
200  if (user != null)
201  user.ControllingClient.SendBulkUpdateInventory(folderCopy);
202 
203  // HACK!!
204  // Insert the ID of the copied folder into the IM so that we know which item to move to trash if it
205  // is rejected.
206  // XXX: This is probably a misuse of the session ID slot.
207  im.imSessionID = copyID.Guid;
208  }
209  else
210  {
211  // First byte of the array is probably the item type
212  // Next 16 bytes are the UUID
213 
214  UUID itemID = new UUID(im.binaryBucket, 1);
215 
216  m_log.DebugFormat("[INVENTORY TRANSFER]: (giving) Inserting item {0} "+
217  "into agent {1}'s inventory",
218  itemID, new UUID(im.toAgentID));
219 
220  string message;
221  InventoryItemBase itemCopy = scene.GiveInventoryItem(new UUID(im.toAgentID), client.AgentId, itemID, out message);
222 
223  if (itemCopy == null)
224  {
225  client.SendAgentAlertMessage(message, false);
226  return;
227  }
228 
229  copyID = itemCopy.ID;
230  Array.Copy(copyID.GetBytes(), 0, im.binaryBucket, 1, 16);
231 
232  if (user != null)
233  user.ControllingClient.SendBulkUpdateInventory(itemCopy);
234 
235  // HACK!!
236  // Insert the ID of the copied item into the IM so that we know which item to move to trash if it
237  // is rejected.
238  // XXX: This is probably a misuse of the session ID slot.
239  im.imSessionID = copyID.Guid;
240  }
241 
242  im.offline = 0;
243 
244  // Send the IM to the recipient. The item is already
245  // in their inventory, so it will not be lost if
246  // they are offline.
247  //
248  if (user != null)
249  {
250  user.ControllingClient.SendInstantMessage(im);
251  return;
252  }
253  else
254  {
255  if (m_TransferModule != null)
256  m_TransferModule.SendInstantMessage(im, delegate(bool success)
257  {
258  if (!success)
259  client.SendAlertMessage("User not online. Inventory has been saved");
260  });
261  }
262  }
263  else if (im.dialog == (byte) InstantMessageDialog.InventoryAccepted ||
264  im.dialog == (byte) InstantMessageDialog.TaskInventoryAccepted)
265  {
266  UUID inventoryID = new UUID(im.imSessionID); // The inventory item/folder, back from it's trip
267  IInventoryService invService = scene.InventoryService;
268 
269  // Special case: folder redirect.
270  // RLV uses this
271  if (im.dialog == (byte) InstantMessageDialog.TaskInventoryAccepted)
272  {
273  InventoryFolderBase folder = new InventoryFolderBase(inventoryID, client.AgentId);
274  folder = invService.GetFolder(folder);
275 
276  if (folder != null)
277  {
278  if (im.binaryBucket.Length >= 16)
279  {
280  UUID destFolderID = new UUID(im.binaryBucket, 0);
281  if (destFolderID != UUID.Zero)
282  {
283  InventoryFolderBase destFolder = new InventoryFolderBase(destFolderID, client.AgentId);
284  destFolder = invService.GetFolder(destFolder);
285  if (destFolder != null)
286  {
287  if (folder.ParentID != destFolder.ID)
288  {
289  folder.ParentID = destFolder.ID;
290  invService.MoveFolder(folder);
291  client.SendBulkUpdateInventory(folder);
292  }
293  }
294  }
295  }
296  }
297  }
298 
299  ScenePresence user = scene.GetScenePresence(new UUID(im.toAgentID));
300 
301  if (user != null) // Local
302  {
303  user.ControllingClient.SendInstantMessage(im);
304  }
305  else
306  {
307  if (m_TransferModule != null)
308  m_TransferModule.SendInstantMessage(im, delegate(bool success) {});
309  }
310  }
311 
312  // XXX: This code was placed here to try and accomodate RLV which moves given folders named #RLV/~<name>
313  // to the requested folder, which in this case is #RLV. However, it is the viewer that appears to be
314  // response from renaming the #RLV/~example folder to ~example. For some reason this is not yet
315  // happening, possibly because we are not sending the correct inventory update messages with the correct
316  // transaction IDs
317  else if (im.dialog == (byte) InstantMessageDialog.TaskInventoryAccepted)
318  {
319  UUID destinationFolderID = UUID.Zero;
320 
321  if (im.binaryBucket != null && im.binaryBucket.Length >= 16)
322  {
323  destinationFolderID = new UUID(im.binaryBucket, 0);
324  }
325 
326  if (destinationFolderID != UUID.Zero)
327  {
328  InventoryFolderBase destinationFolder = new InventoryFolderBase(destinationFolderID, client.AgentId);
329  if (destinationFolder == null)
330  {
331  m_log.WarnFormat(
332  "[INVENTORY TRANSFER]: TaskInventoryAccepted message from {0} in {1} specified folder {2} which does not exist",
333  client.Name, scene.Name, destinationFolderID);
334 
335  return;
336  }
337 
338  IInventoryService invService = scene.InventoryService;
339 
340  UUID inventoryID = new UUID(im.imSessionID); // The inventory item/folder, back from it's trip
341 
342  InventoryItemBase item = new InventoryItemBase(inventoryID, client.AgentId);
343  item = invService.GetItem(item);
344  InventoryFolderBase folder = null;
345  UUID? previousParentFolderID = null;
346 
347  if (item != null) // It's an item
348  {
349  previousParentFolderID = item.Folder;
350  item.Folder = destinationFolderID;
351 
352  invService.DeleteItems(item.Owner, new List<UUID>() { item.ID });
353  scene.AddInventoryItem(client, item);
354  }
355  else
356  {
357  folder = new InventoryFolderBase(inventoryID, client.AgentId);
358  folder = invService.GetFolder(folder);
359 
360  if (folder != null) // It's a folder
361  {
362  previousParentFolderID = folder.ParentID;
363  folder.ParentID = destinationFolderID;
364  invService.MoveFolder(folder);
365  }
366  }
367 
368  // Tell client about updates to original parent and new parent (this should probably be factored with existing move item/folder code).
369  if (previousParentFolderID != null)
370  {
371  InventoryFolderBase previousParentFolder
372  = new InventoryFolderBase((UUID)previousParentFolderID, client.AgentId);
373  previousParentFolder = invService.GetFolder(previousParentFolder);
374  scene.SendInventoryUpdate(client, previousParentFolder, true, true);
375 
376  scene.SendInventoryUpdate(client, destinationFolder, true, true);
377  }
378  }
379  }
380  else if (
381  im.dialog == (byte)InstantMessageDialog.InventoryDeclined
382  || im.dialog == (byte)InstantMessageDialog.TaskInventoryDeclined)
383  {
384  // Here, the recipient is local and we can assume that the
385  // inventory is loaded. Courtesy of the above bulk update,
386  // It will have been pushed to the client, too
387  //
388  IInventoryService invService = scene.InventoryService;
389 
390  InventoryFolderBase trashFolder =
391  invService.GetFolderForType(client.AgentId, FolderType.Trash);
392 
393  UUID inventoryID = new UUID(im.imSessionID); // The inventory item/folder, back from it's trip
394 
395  InventoryItemBase item = new InventoryItemBase(inventoryID, client.AgentId);
396  item = invService.GetItem(item);
397  InventoryFolderBase folder = null;
398  UUID? previousParentFolderID = null;
399 
400  if (item != null && trashFolder != null)
401  {
402  previousParentFolderID = item.Folder;
403  item.Folder = trashFolder.ID;
404 
405  // Diva comment: can't we just update this item???
406  List<UUID> uuids = new List<UUID>();
407  uuids.Add(item.ID);
408  invService.DeleteItems(item.Owner, uuids);
409  scene.AddInventoryItem(client, item);
410  }
411  else
412  {
413  folder = new InventoryFolderBase(inventoryID, client.AgentId);
414  folder = invService.GetFolder(folder);
415 
416  if (folder != null & trashFolder != null)
417  {
418  previousParentFolderID = folder.ParentID;
419  folder.ParentID = trashFolder.ID;
420  invService.MoveFolder(folder);
421  client.SendBulkUpdateInventory(folder);
422  }
423  }
424 
425  if ((null == item && null == folder) | null == trashFolder)
426  {
427  string reason = String.Empty;
428 
429  if (trashFolder == null)
430  reason += " Trash folder not found.";
431  if (item == null)
432  reason += " Item not found.";
433  if (folder == null)
434  reason += " Folder not found.";
435 
436  client.SendAgentAlertMessage("Unable to delete "+
437  "received inventory" + reason, false);
438  }
439  // Tell client about updates to original parent and new parent (this should probably be factored with existing move item/folder code).
440  else if (previousParentFolderID != null)
441  {
442  InventoryFolderBase previousParentFolder
443  = new InventoryFolderBase((UUID)previousParentFolderID, client.AgentId);
444  previousParentFolder = invService.GetFolder(previousParentFolder);
445  scene.SendInventoryUpdate(client, previousParentFolder, true, true);
446 
447  scene.SendInventoryUpdate(client, trashFolder, true, true);
448  }
449 
450  if (im.dialog == (byte)InstantMessageDialog.InventoryDeclined)
451  {
452  ScenePresence user = scene.GetScenePresence(new UUID(im.toAgentID));
453 
454  if (user != null) // Local
455  {
456  user.ControllingClient.SendInstantMessage(im);
457  }
458  else
459  {
460  if (m_TransferModule != null)
461  m_TransferModule.SendInstantMessage(im, delegate(bool success) { });
462  }
463  }
464  }
465  }
466 
471  private void OnGridInstantMessage(GridInstantMessage im)
472  {
473  // Check if this is ours to handle
474  //
475  Scene scene = FindClientScene(new UUID(im.toAgentID));
476 
477  if (scene == null)
478  return;
479 
480  // Find agent to deliver to
481  //
482  ScenePresence user = scene.GetScenePresence(new UUID(im.toAgentID));
483  if (user == null)
484  return;
485 
486  // This requires a little bit of processing because we have to make the
487  // new item visible in the recipient's inventory here
488  //
489  if (im.dialog == (byte) InstantMessageDialog.InventoryOffered)
490  {
491  if (im.binaryBucket.Length < 17) // Invalid
492  return;
493 
494  UUID recipientID = new UUID(im.toAgentID);
495 
496  // First byte is the asset type
497  AssetType assetType = (AssetType)im.binaryBucket[0];
498 
499  if (AssetType.Folder == assetType)
500  {
501  UUID folderID = new UUID(im.binaryBucket, 1);
502 
503  InventoryFolderBase given =
504  new InventoryFolderBase(folderID, recipientID);
505  InventoryFolderBase folder =
506  scene.InventoryService.GetFolder(given);
507 
508  if (folder != null)
509  user.ControllingClient.SendBulkUpdateInventory(folder);
510  }
511  else
512  {
513  UUID itemID = new UUID(im.binaryBucket, 1);
514 
515  InventoryItemBase given =
516  new InventoryItemBase(itemID, recipientID);
517  InventoryItemBase item =
518  scene.InventoryService.GetItem(given);
519 
520  if (item != null)
521  {
522  user.ControllingClient.SendBulkUpdateInventory(item);
523  }
524  }
525  user.ControllingClient.SendInstantMessage(im);
526  }
527  if (im.dialog == (byte) InstantMessageDialog.TaskInventoryOffered)
528  {
529  if (im.binaryBucket.Length < 1) // Invalid
530  return;
531 
532  UUID recipientID = new UUID(im.toAgentID);
533 
534  // Bucket is the asset type
535  AssetType assetType = (AssetType)im.binaryBucket[0];
536 
537  if (AssetType.Folder == assetType)
538  {
539  UUID folderID = new UUID(im.imSessionID);
540 
541  InventoryFolderBase given =
542  new InventoryFolderBase(folderID, recipientID);
543  InventoryFolderBase folder =
544  scene.InventoryService.GetFolder(given);
545 
546  if (folder != null)
547  user.ControllingClient.SendBulkUpdateInventory(folder);
548  }
549  else
550  {
551  UUID itemID = new UUID(im.imSessionID);
552 
553  InventoryItemBase given =
554  new InventoryItemBase(itemID, recipientID);
555  InventoryItemBase item =
556  scene.InventoryService.GetItem(given);
557 
558  if (item != null)
559  {
560  user.ControllingClient.SendBulkUpdateInventory(item);
561  }
562  }
563 
564  // Fix up binary bucket since this may be 17 chars long here
565  Byte[] bucket = new Byte[1];
566  bucket[0] = im.binaryBucket[0];
567  im.binaryBucket = bucket;
568 
569  user.ControllingClient.SendInstantMessage(im);
570  }
571  else if (im.dialog == (byte) InstantMessageDialog.InventoryAccepted ||
572  im.dialog == (byte) InstantMessageDialog.InventoryDeclined ||
573  im.dialog == (byte) InstantMessageDialog.TaskInventoryDeclined ||
574  im.dialog == (byte) InstantMessageDialog.TaskInventoryAccepted)
575  {
576  user.ControllingClient.SendInstantMessage(im);
577  }
578  }
579  }
580 }
void Close()
This is the inverse to Initialise. After a Close(), this instance won't be usable anymore...
void AddRegion(Scene scene)
This is called whenever a Scene is added. For shared modules, this can happen several times...
Inventory Item - contains all the properties associated with an individual inventory piece...
void RegionLoaded(Scene scene)
This will be called once for every scene loaded. In a shared module this will be multiple times in on...
Interactive OpenSim region server
Definition: OpenSim.cs:55
UUID ID
A UUID containing the ID for the inventory node itself
void PostInitialise()
This is called exactly once after all the shared region-modules have been instanciated and IRegionMod...
void RemoveRegion(Scene scene)
This is called whenever a Scene is removed. For shared modules, this can happen several times...
void Initialise(IConfigSource config)
This is called to initialize the region module. For shared modules, this is called exactly once...