corrade-vassal – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 vero 1 /*
2 * Copyright (c) 2006-2014, openmetaverse.org
3 * All rights reserved.
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 *
8 * - Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 * - Neither the name of the openmetaverse.org nor the names
11 * of its contributors may be used to endorse or promote products derived from
12 * this software without specific prior written permission.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
18 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 */
26  
27 using System;
28 using System.Collections.Generic;
29 using System.Net;
30 using System.Text.RegularExpressions;
31 using System.Threading;
32 using System.Text;
33 using System.Runtime.Serialization;
34 using OpenMetaverse.Http;
35 using OpenMetaverse.Messages.Linden;
36 using OpenMetaverse.StructuredData;
37 using OpenMetaverse.Packets;
38  
39 namespace OpenMetaverse
40 {
41 #region Enums
42  
43 [Flags]
44 public enum InventorySortOrder : int
45 {
46 /// <summary>Sort by name</summary>
47 ByName = 0,
48 /// <summary>Sort by date</summary>
49 ByDate = 1,
50 /// <summary>Sort folders by name, regardless of whether items are
51 /// sorted by name or date</summary>
52 FoldersByName = 2,
53 /// <summary>Place system folders at the top</summary>
54 SystemFoldersToTop = 4
55 }
56  
57 /// <summary>
58 /// Possible destinations for DeRezObject request
59 /// </summary>
60 public enum DeRezDestination : byte
61 {
62 /// <summary></summary>
63 AgentInventorySave = 0,
64 /// <summary>Copy from in-world to agent inventory</summary>
65 AgentInventoryCopy = 1,
66 /// <summary>Derez to TaskInventory</summary>
67 TaskInventory = 2,
68 /// <summary></summary>
69 Attachment = 3,
70 /// <summary>Take Object</summary>
71 AgentInventoryTake = 4,
72 /// <summary></summary>
73 ForceToGodInventory = 5,
74 /// <summary>Delete Object</summary>
75 TrashFolder = 6,
76 /// <summary>Put an avatar attachment into agent inventory</summary>
77 AttachmentToInventory = 7,
78 /// <summary></summary>
79 AttachmentExists = 8,
80 /// <summary>Return an object back to the owner's inventory</summary>
81 ReturnToOwner = 9,
82 /// <summary>Return a deeded object back to the last owner's inventory</summary>
83 ReturnToLastOwner = 10
84 }
85  
86 /// <summary>
87 /// Upper half of the Flags field for inventory items
88 /// </summary>
89 [Flags]
90 public enum InventoryItemFlags : uint
91 {
92 None = 0,
93 /// <summary>Indicates that the NextOwner permission will be set to the
94 /// most restrictive set of permissions found in the object set
95 /// (including linkset items and object inventory items) on next rez</summary>
96 ObjectSlamPerm = 0x100,
97 /// <summary>Indicates that the object sale information has been
98 /// changed</summary>
99 ObjectSlamSale = 0x1000,
100 /// <summary>If set, and a slam bit is set, indicates BaseMask will be overwritten on Rez</summary>
101 ObjectOverwriteBase = 0x010000,
102 /// <summary>If set, and a slam bit is set, indicates OwnerMask will be overwritten on Rez</summary>
103 ObjectOverwriteOwner = 0x020000,
104 /// <summary>If set, and a slam bit is set, indicates GroupMask will be overwritten on Rez</summary>
105 ObjectOverwriteGroup = 0x040000,
106 /// <summary>If set, and a slam bit is set, indicates EveryoneMask will be overwritten on Rez</summary>
107 ObjectOverwriteEveryone = 0x080000,
108 /// <summary>If set, and a slam bit is set, indicates NextOwnerMask will be overwritten on Rez</summary>
109 ObjectOverwriteNextOwner = 0x100000,
110 /// <summary>Indicates whether this object is composed of multiple
111 /// items or not</summary>
112 ObjectHasMultipleItems = 0x200000,
113 /// <summary>Indicates that the asset is only referenced by this
114 /// inventory item. If this item is deleted or updated to reference a
115 /// new assetID, the asset can be deleted</summary>
116 SharedSingleReference = 0x40000000,
117 }
118  
119 #endregion Enums
120  
121 #region Inventory Object Classes
122  
123 /// <summary>
124 /// Base Class for Inventory Items
125 /// </summary>
126 [Serializable()]
127 public abstract class InventoryBase : ISerializable
128 {
129 /// <summary><seealso cref="OpenMetaverse.UUID"/> of item/folder</summary>
130 public UUID UUID;
131 /// <summary><seealso cref="OpenMetaverse.UUID"/> of parent folder</summary>
132 public UUID ParentUUID;
133 /// <summary>Name of item/folder</summary>
134 public string Name;
135 /// <summary>Item/Folder Owners <seealso cref="OpenMetaverse.UUID"/></summary>
136 public UUID OwnerID;
137  
138 /// <summary>
139 /// Constructor, takes an itemID as a parameter
140 /// </summary>
141 /// <param name="itemID">The <seealso cref="OpenMetaverse.UUID"/> of the item</param>
142 public InventoryBase(UUID itemID)
143 {
144 if (itemID == UUID.Zero)
145 Logger.Log("Initializing an InventoryBase with UUID.Zero", Helpers.LogLevel.Warning);
146 UUID = itemID;
147 }
148  
149 /// <summary>
150 ///
151 /// </summary>
152 /// <returns></returns>
153 public virtual void GetObjectData(SerializationInfo info, StreamingContext ctxt)
154 {
155 info.AddValue("UUID", UUID);
156 info.AddValue("ParentUUID", ParentUUID);
157 info.AddValue("Name", Name);
158 info.AddValue("OwnerID", OwnerID);
159 }
160  
161 /// <summary>
162 ///
163 /// </summary>
164 /// <returns></returns>
165 public InventoryBase(SerializationInfo info, StreamingContext ctxt)
166 {
167 UUID = (UUID)info.GetValue("UUID", typeof(UUID));
168 ParentUUID = (UUID)info.GetValue("ParentUUID", typeof(UUID));
169 Name = (string)info.GetValue("Name", typeof(string));
170 OwnerID = (UUID)info.GetValue("OwnerID", typeof(UUID));
171 }
172  
173 /// <summary>
174 /// Generates a number corresponding to the value of the object to support the use of a hash table,
175 /// suitable for use in hashing algorithms and data structures such as a hash table
176 /// </summary>
177 /// <returns>A Hashcode of all the combined InventoryBase fields</returns>
178 public override int GetHashCode()
179 {
180 return UUID.GetHashCode() ^ ParentUUID.GetHashCode() ^ Name.GetHashCode() ^ OwnerID.GetHashCode();
181 }
182  
183 /// <summary>
184 /// Determine whether the specified <seealso cref="OpenMetaverse.InventoryBase"/> object is equal to the current object
185 /// </summary>
186 /// <param name="o">InventoryBase object to compare against</param>
187 /// <returns>true if objects are the same</returns>
188 public override bool Equals(object o)
189 {
190 InventoryBase inv = o as InventoryBase;
191 return inv != null && Equals(inv);
192 }
193  
194 /// <summary>
195 /// Determine whether the specified <seealso cref="OpenMetaverse.InventoryBase"/> object is equal to the current object
196 /// </summary>
197 /// <param name="o">InventoryBase object to compare against</param>
198 /// <returns>true if objects are the same</returns>
199 public virtual bool Equals(InventoryBase o)
200 {
201 return o.UUID == UUID
202 && o.ParentUUID == ParentUUID
203 && o.Name == Name
204 && o.OwnerID == OwnerID;
205 }
206  
207 /// <summary>
208 /// Convert inventory to OSD
209 /// </summary>
210 /// <returns>OSD representation</returns>
211 public abstract OSD GetOSD();
212 }
213  
214 /// <summary>
215 /// An Item in Inventory
216 /// </summary>
217 [Serializable()]
218 public class InventoryItem : InventoryBase
219 {
220 public override string ToString()
221 {
222 return AssetType + " " + AssetUUID + " (" + InventoryType + " " + UUID + ") '" + Name + "'/'" +
223 Description + "' " + Permissions;
224 }
225 /// <summary>The <seealso cref="OpenMetaverse.UUID"/> of this item</summary>
226 public UUID AssetUUID;
227 /// <summary>The combined <seealso cref="OpenMetaverse.Permissions"/> of this item</summary>
228 public Permissions Permissions;
229 /// <summary>The type of item from <seealso cref="OpenMetaverse.AssetType"/></summary>
230 public AssetType AssetType;
231 /// <summary>The type of item from the <seealso cref="OpenMetaverse.InventoryType"/> enum</summary>
232 public InventoryType InventoryType;
233 /// <summary>The <seealso cref="OpenMetaverse.UUID"/> of the creator of this item</summary>
234 public UUID CreatorID;
235 /// <summary>A Description of this item</summary>
236 public string Description;
237 /// <summary>The <seealso cref="OpenMetaverse.Group"/>s <seealso cref="OpenMetaverse.UUID"/> this item is set to or owned by</summary>
238 public UUID GroupID;
239 /// <summary>If true, item is owned by a group</summary>
240 public bool GroupOwned;
241 /// <summary>The price this item can be purchased for</summary>
242 public int SalePrice;
243 /// <summary>The type of sale from the <seealso cref="OpenMetaverse.SaleType"/> enum</summary>
244 public SaleType SaleType;
245 /// <summary>Combined flags from <seealso cref="OpenMetaverse.InventoryItemFlags"/></summary>
246 public uint Flags;
247 /// <summary>Time and date this inventory item was created, stored as
248 /// UTC (Coordinated Universal Time)</summary>
249 public DateTime CreationDate;
250 /// <summary>Used to update the AssetID in requests sent to the server</summary>
251 public UUID TransactionID;
252 /// <summary>The <seealso cref="OpenMetaverse.UUID"/> of the previous owner of the item</summary>
253 public UUID LastOwnerID;
254  
255 /// <summary>
256 /// Construct a new InventoryItem object
257 /// </summary>
258 /// <param name="itemID">The <seealso cref="OpenMetaverse.UUID"/> of the item</param>
259 public InventoryItem(UUID itemID)
260 : base(itemID) { }
261  
262 /// <summary>
263 /// Construct a new InventoryItem object of a specific Type
264 /// </summary>
265 /// <param name="type">The type of item from <seealso cref="OpenMetaverse.InventoryType"/></param>
266 /// <param name="itemID"><seealso cref="OpenMetaverse.UUID"/> of the item</param>
267 public InventoryItem(InventoryType type, UUID itemID) : base(itemID) { InventoryType = type; }
268  
269 /// <summary>
270 /// Indicates inventory item is a link
271 /// </summary>
272 /// <returns>True if inventory item is a link to another inventory item</returns>
273 public bool IsLink()
274 {
275 return AssetType == AssetType.Link || AssetType == AssetType.LinkFolder;
276 }
277  
278 /// <summary>
279 ///
280 /// </summary>
281 /// <returns></returns>
282 override public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
283 {
284 base.GetObjectData(info, ctxt);
285 info.AddValue("AssetUUID", AssetUUID, typeof(UUID));
286 info.AddValue("Permissions", Permissions, typeof(Permissions));
287 info.AddValue("AssetType", AssetType);
288 info.AddValue("InventoryType", InventoryType);
289 info.AddValue("CreatorID", CreatorID);
290 info.AddValue("Description", Description);
291 info.AddValue("GroupID", GroupID);
292 info.AddValue("GroupOwned", GroupOwned);
293 info.AddValue("SalePrice", SalePrice);
294 info.AddValue("SaleType", SaleType);
295 info.AddValue("Flags", Flags);
296 info.AddValue("CreationDate", CreationDate);
297 info.AddValue("LastOwnerID", LastOwnerID);
298 }
299  
300 /// <summary>
301 ///
302 /// </summary>
303 /// <returns></returns>
304 public InventoryItem(SerializationInfo info, StreamingContext ctxt)
305 : base(info, ctxt)
306 {
307 AssetUUID = (UUID)info.GetValue("AssetUUID", typeof(UUID));
308 Permissions = (Permissions)info.GetValue("Permissions", typeof(Permissions));
309 AssetType = (AssetType)info.GetValue("AssetType", typeof(AssetType));
310 InventoryType = (InventoryType)info.GetValue("InventoryType", typeof(InventoryType));
311 CreatorID = (UUID)info.GetValue("CreatorID", typeof(UUID));
312 Description = (string)info.GetValue("Description", typeof(string));
313 GroupID = (UUID)info.GetValue("GroupID", typeof(UUID));
314 GroupOwned = (bool)info.GetValue("GroupOwned", typeof(bool));
315 SalePrice = (int)info.GetValue("SalePrice", typeof(int));
316 SaleType = (SaleType)info.GetValue("SaleType", typeof(SaleType));
317 Flags = (uint)info.GetValue("Flags", typeof(uint));
318 CreationDate = (DateTime)info.GetValue("CreationDate", typeof(DateTime));
319 LastOwnerID = (UUID)info.GetValue("LastOwnerID", typeof(UUID));
320 }
321  
322 /// <summary>
323 /// Generates a number corresponding to the value of the object to support the use of a hash table.
324 /// Suitable for use in hashing algorithms and data structures such as a hash table
325 /// </summary>
326 /// <returns>A Hashcode of all the combined InventoryItem fields</returns>
327 public override int GetHashCode()
328 {
329 return AssetUUID.GetHashCode() ^ Permissions.GetHashCode() ^ AssetType.GetHashCode() ^
330 InventoryType.GetHashCode() ^ Description.GetHashCode() ^ GroupID.GetHashCode() ^
331 GroupOwned.GetHashCode() ^ SalePrice.GetHashCode() ^ SaleType.GetHashCode() ^
332 Flags.GetHashCode() ^ CreationDate.GetHashCode() ^ LastOwnerID.GetHashCode();
333 }
334  
335 /// <summary>
336 /// Compares an object
337 /// </summary>
338 /// <param name="o">The object to compare</param>
339 /// <returns>true if comparison object matches</returns>
340 public override bool Equals(object o)
341 {
342 InventoryItem item = o as InventoryItem;
343 return item != null && Equals(item);
344 }
345  
346 /// <summary>
347 /// Determine whether the specified <seealso cref="OpenMetaverse.InventoryBase"/> object is equal to the current object
348 /// </summary>
349 /// <param name="o">The <seealso cref="OpenMetaverse.InventoryBase"/> object to compare against</param>
350 /// <returns>true if objects are the same</returns>
351 public override bool Equals(InventoryBase o)
352 {
353 InventoryItem item = o as InventoryItem;
354 return item != null && Equals(item);
355 }
356  
357 /// <summary>
358 /// Determine whether the specified <seealso cref="OpenMetaverse.InventoryItem"/> object is equal to the current object
359 /// </summary>
360 /// <param name="o">The <seealso cref="OpenMetaverse.InventoryItem"/> object to compare against</param>
361 /// <returns>true if objects are the same</returns>
362 public bool Equals(InventoryItem o)
363 {
364 return base.Equals(o as InventoryBase)
365 && o.AssetType == AssetType
366 && o.AssetUUID == AssetUUID
367 && o.CreationDate == CreationDate
368 && o.Description == Description
369 && o.Flags == Flags
370 && o.GroupID == GroupID
371 && o.GroupOwned == GroupOwned
372 && o.InventoryType == InventoryType
373 && o.Permissions.Equals(Permissions)
374 && o.SalePrice == SalePrice
375 && o.SaleType == SaleType
376 && o.LastOwnerID == LastOwnerID;
377 }
378  
379 /// <summary>
380 /// Create InventoryItem from OSD
381 /// </summary>
382 /// <param name="data">OSD Data that makes up InventoryItem</param>
383 /// <returns>Inventory item created</returns>
384 public static InventoryItem FromOSD(OSD data)
385 {
386 OSDMap descItem = (OSDMap)data;
387  
388 InventoryType type = (InventoryType)descItem["inv_type"].AsInteger();
389 if (type == InventoryType.Texture && (AssetType)descItem["type"].AsInteger() == AssetType.Object)
390 {
391 type = InventoryType.Attachment;
392 }
393 InventoryItem item = InventoryManager.CreateInventoryItem(type, descItem["item_id"]);
394  
395 item.ParentUUID = descItem["parent_id"];
396 item.Name = descItem["name"];
397 item.Description = descItem["desc"];
398 item.OwnerID = descItem["agent_id"];
399 item.ParentUUID = descItem["parent_id"];
400 item.AssetUUID = descItem["asset_id"];
401 item.AssetType = (AssetType)descItem["type"].AsInteger();
402 item.CreationDate = Utils.UnixTimeToDateTime(descItem["created_at"]);
403 item.Flags = descItem["flags"];
404  
405 OSDMap perms = (OSDMap)descItem["permissions"];
406 item.CreatorID = perms["creator_id"];
407 item.LastOwnerID = perms["last_owner_id"];
408 item.Permissions = new Permissions(perms["base_mask"], perms["everyone_mask"], perms["group_mask"], perms["next_owner_mask"], perms["owner_mask"]);
409 item.GroupOwned = perms["is_owner_group"];
410 item.GroupID = perms["group_id"];
411  
412 OSDMap sale = (OSDMap)descItem["sale_info"];
413 item.SalePrice = sale["sale_price"];
414 item.SaleType = (SaleType)sale["sale_type"].AsInteger();
415  
416 return item;
417 }
418  
419 /// <summary>
420 /// Convert InventoryItem to OSD
421 /// </summary>
422 /// <returns>OSD representation of InventoryItem</returns>
423 public override OSD GetOSD()
424 {
425 OSDMap map = new OSDMap();
426 map["inv_type"] = (int)InventoryType;
427 map["parent_id"] = ParentUUID;
428 map["name"] = Name;
429 map["desc"] = Description;
430 map["agent_id"] = OwnerID;
431 map["parent_id"] = ParentUUID;
432 map["asset_id"] = AssetUUID;
433 map["type"] = (int)AssetType;
434 map["created_at"] = CreationDate;
435 map["flags"] = Flags;
436  
437 OSDMap perms = (OSDMap)Permissions.GetOSD();
438 perms["creator_id"] = CreatorID;
439 perms["last_owner_id"] = LastOwnerID;
440 perms["is_owner_group"] = GroupOwned;
441 perms["group_id"] = GroupID;
442 map["permissions"] = perms;
443  
444 OSDMap sale = new OSDMap();
445 sale["sale_price"] = SalePrice;
446 sale["sale_type"] = (int)SaleType;
447 map["sale_info"] = sale;
448  
449 return map;
450 }
451 }
452  
453 /// <summary>
454 /// InventoryTexture Class representing a graphical image
455 /// </summary>
456 /// <seealso cref="ManagedImage"/>
457 [Serializable()]
458 public class InventoryTexture : InventoryItem
459 {
460 /// <summary>
461 /// Construct an InventoryTexture object
462 /// </summary>
463 /// <param name="itemID">A <seealso cref="OpenMetaverse.UUID"/> which becomes the
464 /// <seealso cref="OpenMetaverse.InventoryItem"/> objects AssetUUID</param>
465 public InventoryTexture(UUID itemID)
466 : base(itemID)
467 {
468 InventoryType = InventoryType.Texture;
469 }
470  
471 /// <summary>
472 /// Construct an InventoryTexture object from a serialization stream
473 /// </summary>
474 public InventoryTexture(SerializationInfo info, StreamingContext ctxt)
475 : base(info, ctxt)
476 {
477 InventoryType = InventoryType.Texture;
478 }
479 }
480  
481 /// <summary>
482 /// InventorySound Class representing a playable sound
483 /// </summary>
484 [Serializable()]
485 public class InventorySound : InventoryItem
486 {
487 /// <summary>
488 /// Construct an InventorySound object
489 /// </summary>
490 /// <param name="itemID">A <seealso cref="OpenMetaverse.UUID"/> which becomes the
491 /// <seealso cref="OpenMetaverse.InventoryItem"/> objects AssetUUID</param>
492 public InventorySound(UUID itemID)
493 : base(itemID)
494 {
495 InventoryType = InventoryType.Sound;
496 }
497  
498 /// <summary>
499 /// Construct an InventorySound object from a serialization stream
500 /// </summary>
501 public InventorySound(SerializationInfo info, StreamingContext ctxt)
502 : base(info, ctxt)
503 {
504 InventoryType = InventoryType.Sound;
505 }
506 }
507  
508 /// <summary>
509 /// InventoryCallingCard Class, contains information on another avatar
510 /// </summary>
511 [Serializable()]
512 public class InventoryCallingCard : InventoryItem
513 {
514 /// <summary>
515 /// Construct an InventoryCallingCard object
516 /// </summary>
517 /// <param name="itemID">A <seealso cref="OpenMetaverse.UUID"/> which becomes the
518 /// <seealso cref="OpenMetaverse.InventoryItem"/> objects AssetUUID</param>
519 public InventoryCallingCard(UUID itemID)
520 : base(itemID)
521 {
522 InventoryType = InventoryType.CallingCard;
523 }
524  
525 /// <summary>
526 /// Construct an InventoryCallingCard object from a serialization stream
527 /// </summary>
528 public InventoryCallingCard(SerializationInfo info, StreamingContext ctxt)
529 : base(info, ctxt)
530 {
531 InventoryType = InventoryType.CallingCard;
532 }
533 }
534  
535 /// <summary>
536 /// InventoryLandmark Class, contains details on a specific location
537 /// </summary>
538 [Serializable()]
539 public class InventoryLandmark : InventoryItem
540 {
541 /// <summary>
542 /// Construct an InventoryLandmark object
543 /// </summary>
544 /// <param name="itemID">A <seealso cref="OpenMetaverse.UUID"/> which becomes the
545 /// <seealso cref="OpenMetaverse.InventoryItem"/> objects AssetUUID</param>
546 public InventoryLandmark(UUID itemID)
547 : base(itemID)
548 {
549 InventoryType = InventoryType.Landmark;
550 }
551  
552 /// <summary>
553 /// Construct an InventoryLandmark object from a serialization stream
554 /// </summary>
555 public InventoryLandmark(SerializationInfo info, StreamingContext ctxt)
556 : base(info, ctxt)
557 {
558 InventoryType = InventoryType.Landmark;
559  
560 }
561  
562 /// <summary>
563 /// Landmarks use the InventoryItemFlags struct and will have a flag of 1 set if they have been visited
564 /// </summary>
565 public bool LandmarkVisited
566 {
567 get { return (Flags & 1) != 0; }
568 set
569 {
570 if (value) Flags |= 1;
571 else Flags &= ~1u;
572 }
573 }
574 }
575  
576 /// <summary>
577 /// InventoryObject Class contains details on a primitive or coalesced set of primitives
578 /// </summary>
579 [Serializable()]
580 public class InventoryObject : InventoryItem
581 {
582 /// <summary>
583 /// Construct an InventoryObject object
584 /// </summary>
585 /// <param name="itemID">A <seealso cref="OpenMetaverse.UUID"/> which becomes the
586 /// <seealso cref="OpenMetaverse.InventoryItem"/> objects AssetUUID</param>
587 public InventoryObject(UUID itemID)
588 : base(itemID)
589 {
590 InventoryType = InventoryType.Object;
591 }
592  
593 /// <summary>
594 /// Construct an InventoryObject object from a serialization stream
595 /// </summary>
596 public InventoryObject(SerializationInfo info, StreamingContext ctxt)
597 : base(info, ctxt)
598 {
599 InventoryType = InventoryType.Object;
600 }
601  
602 /// <summary>
603 /// Gets or sets the upper byte of the Flags value
604 /// </summary>
605 public InventoryItemFlags ItemFlags
606 {
607 get { return (InventoryItemFlags)(Flags & ~0xFF); }
608 set { Flags = (uint)value | (Flags & 0xFF); }
609 }
610  
611 /// <summary>
612 /// Gets or sets the object attachment point, the lower byte of the Flags value
613 /// </summary>
614 public AttachmentPoint AttachPoint
615 {
616 get { return (AttachmentPoint)(Flags & 0xFF); }
617 set { Flags = (uint)value | (Flags & 0xFFFFFF00); }
618 }
619 }
620  
621 /// <summary>
622 /// InventoryNotecard Class, contains details on an encoded text document
623 /// </summary>
624 [Serializable()]
625 public class InventoryNotecard : InventoryItem
626 {
627 /// <summary>
628 /// Construct an InventoryNotecard object
629 /// </summary>
630 /// <param name="itemID">A <seealso cref="OpenMetaverse.UUID"/> which becomes the
631 /// <seealso cref="OpenMetaverse.InventoryItem"/> objects AssetUUID</param>
632 public InventoryNotecard(UUID itemID)
633 : base(itemID)
634 {
635 InventoryType = InventoryType.Notecard;
636 }
637  
638 /// <summary>
639 /// Construct an InventoryNotecard object from a serialization stream
640 /// </summary>
641 public InventoryNotecard(SerializationInfo info, StreamingContext ctxt)
642 : base(info, ctxt)
643 {
644 InventoryType = InventoryType.Notecard;
645 }
646 }
647  
648 /// <summary>
649 /// InventoryCategory Class
650 /// </summary>
651 /// <remarks>TODO: Is this even used for anything?</remarks>
652 [Serializable()]
653 public class InventoryCategory : InventoryItem
654 {
655 /// <summary>
656 /// Construct an InventoryCategory object
657 /// </summary>
658 /// <param name="itemID">A <seealso cref="OpenMetaverse.UUID"/> which becomes the
659 /// <seealso cref="OpenMetaverse.InventoryItem"/> objects AssetUUID</param>
660 public InventoryCategory(UUID itemID)
661 : base(itemID)
662 {
663 InventoryType = InventoryType.Category;
664 }
665  
666 /// <summary>
667 /// Construct an InventoryCategory object from a serialization stream
668 /// </summary>
669 public InventoryCategory(SerializationInfo info, StreamingContext ctxt)
670 : base(info, ctxt)
671 {
672 InventoryType = InventoryType.Category;
673 }
674 }
675  
676 /// <summary>
677 /// InventoryLSL Class, represents a Linden Scripting Language object
678 /// </summary>
679 [Serializable()]
680 public class InventoryLSL : InventoryItem
681 {
682 /// <summary>
683 /// Construct an InventoryLSL object
684 /// </summary>
685 /// <param name="itemID">A <seealso cref="OpenMetaverse.UUID"/> which becomes the
686 /// <seealso cref="OpenMetaverse.InventoryItem"/> objects AssetUUID</param>
687 public InventoryLSL(UUID itemID)
688 : base(itemID)
689 {
690 InventoryType = InventoryType.LSL;
691 }
692  
693 /// <summary>
694 /// Construct an InventoryLSL object from a serialization stream
695 /// </summary>
696 public InventoryLSL(SerializationInfo info, StreamingContext ctxt)
697 : base(info, ctxt)
698 {
699 InventoryType = InventoryType.LSL;
700 }
701 }
702  
703 /// <summary>
704 /// InventorySnapshot Class, an image taken with the viewer
705 /// </summary>
706 [Serializable()]
707 public class InventorySnapshot : InventoryItem
708 {
709 /// <summary>
710 /// Construct an InventorySnapshot object
711 /// </summary>
712 /// <param name="itemID">A <seealso cref="OpenMetaverse.UUID"/> which becomes the
713 /// <seealso cref="OpenMetaverse.InventoryItem"/> objects AssetUUID</param>
714 public InventorySnapshot(UUID itemID)
715 : base(itemID)
716 {
717 InventoryType = InventoryType.Snapshot;
718 }
719  
720 /// <summary>
721 /// Construct an InventorySnapshot object from a serialization stream
722 /// </summary>
723 public InventorySnapshot(SerializationInfo info, StreamingContext ctxt)
724 : base(info, ctxt)
725 {
726 InventoryType = InventoryType.Snapshot;
727 }
728 }
729  
730 /// <summary>
731 /// InventoryAttachment Class, contains details on an attachable object
732 /// </summary>
733 [Serializable()]
734 public class InventoryAttachment : InventoryItem
735 {
736 /// <summary>
737 /// Construct an InventoryAttachment object
738 /// </summary>
739 /// <param name="itemID">A <seealso cref="OpenMetaverse.UUID"/> which becomes the
740 /// <seealso cref="OpenMetaverse.InventoryItem"/> objects AssetUUID</param>
741 public InventoryAttachment(UUID itemID)
742 : base(itemID)
743 {
744 InventoryType = InventoryType.Attachment;
745 }
746  
747 /// <summary>
748 /// Construct an InventoryAttachment object from a serialization stream
749 /// </summary>
750 public InventoryAttachment(SerializationInfo info, StreamingContext ctxt)
751 : base(info, ctxt)
752 {
753 InventoryType = InventoryType.Attachment;
754 }
755  
756 /// <summary>
757 /// Get the last AttachmentPoint this object was attached to
758 /// </summary>
759 public AttachmentPoint AttachmentPoint
760 {
761 get { return (AttachmentPoint)Flags; }
762 set { Flags = (uint)value; }
763 }
764 }
765  
766 /// <summary>
767 /// InventoryWearable Class, details on a clothing item or body part
768 /// </summary>
769 [Serializable()]
770 public class InventoryWearable : InventoryItem
771 {
772 /// <summary>
773 /// Construct an InventoryWearable object
774 /// </summary>
775 /// <param name="itemID">A <seealso cref="OpenMetaverse.UUID"/> which becomes the
776 /// <seealso cref="OpenMetaverse.InventoryItem"/> objects AssetUUID</param>
777 public InventoryWearable(UUID itemID) : base(itemID) { InventoryType = InventoryType.Wearable; }
778  
779 /// <summary>
780 /// Construct an InventoryWearable object from a serialization stream
781 /// </summary>
782 public InventoryWearable(SerializationInfo info, StreamingContext ctxt)
783 : base(info, ctxt)
784 {
785 InventoryType = InventoryType.Wearable;
786 }
787  
788 /// <summary>
789 /// The <seealso cref="OpenMetaverse.WearableType"/>, Skin, Shape, Skirt, Etc
790 /// </summary>
791 public WearableType WearableType
792 {
793 get { return (WearableType)Flags; }
794 set { Flags = (uint)value; }
795 }
796 }
797  
798 /// <summary>
799 /// InventoryAnimation Class, A bvh encoded object which animates an avatar
800 /// </summary>
801 [Serializable()]
802 public class InventoryAnimation : InventoryItem
803 {
804 /// <summary>
805 /// Construct an InventoryAnimation object
806 /// </summary>
807 /// <param name="itemID">A <seealso cref="OpenMetaverse.UUID"/> which becomes the
808 /// <seealso cref="OpenMetaverse.InventoryItem"/> objects AssetUUID</param>
809 public InventoryAnimation(UUID itemID)
810 : base(itemID)
811 {
812 InventoryType = InventoryType.Animation;
813 }
814  
815 /// <summary>
816 /// Construct an InventoryAnimation object from a serialization stream
817 /// </summary>
818 public InventoryAnimation(SerializationInfo info, StreamingContext ctxt)
819 : base(info, ctxt)
820 {
821 InventoryType = InventoryType.Animation;
822 }
823  
824  
825 }
826  
827 /// <summary>
828 /// InventoryGesture Class, details on a series of animations, sounds, and actions
829 /// </summary>
830 [Serializable()]
831 public class InventoryGesture : InventoryItem
832 {
833 /// <summary>
834 /// Construct an InventoryGesture object
835 /// </summary>
836 /// <param name="itemID">A <seealso cref="OpenMetaverse.UUID"/> which becomes the
837 /// <seealso cref="OpenMetaverse.InventoryItem"/> objects AssetUUID</param>
838 public InventoryGesture(UUID itemID)
839 : base(itemID)
840 {
841 InventoryType = InventoryType.Gesture;
842 }
843  
844 /// <summary>
845 /// Construct an InventoryGesture object from a serialization stream
846 /// </summary>
847 public InventoryGesture(SerializationInfo info, StreamingContext ctxt)
848 : base(info, ctxt)
849 {
850 InventoryType = InventoryType.Gesture;
851 }
852 }
853  
854 /// <summary>
855 /// A folder contains <seealso cref="T:OpenMetaverse.InventoryItem"/>s and has certain attributes specific
856 /// to itself
857 /// </summary>
858 [Serializable()]
859 public class InventoryFolder : InventoryBase
860 {
861 /// <summary>The Preferred <seealso cref="T:OpenMetaverse.AssetType"/> for a folder.</summary>
862 public AssetType PreferredType;
863 /// <summary>The Version of this folder</summary>
864 public int Version;
865 /// <summary>Number of child items this folder contains.</summary>
866 public int DescendentCount;
867  
868 /// <summary>
869 /// Constructor
870 /// </summary>
871 /// <param name="itemID">UUID of the folder</param>
872 public InventoryFolder(UUID itemID)
873 : base(itemID)
874 {
875 PreferredType = AssetType.Unknown;
876 Version = 1;
877 DescendentCount = 0;
878 }
879  
880 /// <summary>
881 ///
882 /// </summary>
883 /// <returns></returns>
884 public override string ToString()
885 {
886 return Name;
887 }
888  
889 /// <summary>
890 /// Get Serilization data for this InventoryFolder object
891 /// </summary>
892 override public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
893 {
894 base.GetObjectData(info, ctxt);
895 info.AddValue("PreferredType", PreferredType, typeof(AssetType));
896 info.AddValue("Version", Version);
897 info.AddValue("DescendentCount", DescendentCount);
898 }
899  
900 /// <summary>
901 /// Construct an InventoryFolder object from a serialization stream
902 /// </summary>
903 public InventoryFolder(SerializationInfo info, StreamingContext ctxt)
904 : base(info, ctxt)
905 {
906 PreferredType = (AssetType)info.GetValue("PreferredType", typeof(AssetType));
907 Version = (int)info.GetValue("Version", typeof(int));
908 DescendentCount = (int)info.GetValue("DescendentCount", typeof(int));
909 }
910  
911 /// <summary>
912 ///
913 /// </summary>
914 /// <returns></returns>
915 public override int GetHashCode()
916 {
917 return PreferredType.GetHashCode() ^ Version.GetHashCode() ^ DescendentCount.GetHashCode();
918 }
919  
920 /// <summary>
921 ///
922 /// </summary>
923 /// <param name="o"></param>
924 /// <returns></returns>
925 public override bool Equals(object o)
926 {
927 InventoryFolder folder = o as InventoryFolder;
928 return folder != null && Equals(folder);
929 }
930  
931 /// <summary>
932 ///
933 /// </summary>
934 /// <param name="o"></param>
935 /// <returns></returns>
936 public override bool Equals(InventoryBase o)
937 {
938 InventoryFolder folder = o as InventoryFolder;
939 return folder != null && Equals(folder);
940 }
941  
942 /// <summary>
943 ///
944 /// </summary>
945 /// <param name="o"></param>
946 /// <returns></returns>
947 public bool Equals(InventoryFolder o)
948 {
949 return base.Equals(o as InventoryBase)
950 && o.DescendentCount == DescendentCount
951 && o.PreferredType == PreferredType
952 && o.Version == Version;
953 }
954  
955 /// <summary>
956 /// Create InventoryFolder from OSD
957 /// </summary>
958 /// <param name="data">OSD Data that makes up InventoryFolder</param>
959 /// <returns>Inventory folder created</returns>
960 public static InventoryFolder FromOSD(OSD data)
961 {
962 OSDMap res = (OSDMap)data;
963 UUID folderID = res.ContainsKey("category_id") ? res["category_id"] : res["folder_id"];
964 InventoryFolder folder = new InventoryFolder(folderID);
965 folder.Name = res["name"];
966 folder.DescendentCount = res["descendents"];
967 folder.Version = res["version"];
968 folder.OwnerID = res.ContainsKey("agent_id") ? res["agent_id"] : res["owner_id"];
969 folder.ParentUUID = res["parent_id"];
970 folder.PreferredType = (AssetType)(int)res["type_default"];
971 return folder;
972 }
973  
974 /// <summary>
975 /// Convert InventoryItem to OSD
976 /// </summary>
977 /// <returns>OSD representation of InventoryItem</returns>
978 public override OSD GetOSD()
979 {
980 OSDMap res = new OSDMap(4);
981 res["name"] = Name;
982 res["type_default"] = (int)PreferredType;
983 res["folder_id"] = UUID;
984 res["descendents"] = DescendentCount;
985 res["version"] = Version;
986 res["owner_id"] = OwnerID;
987 res["parent_id"] = ParentUUID;
988 return res;
989 }
990  
991 }
992  
993 #endregion Inventory Object Classes
994  
995 /// <summary>
996 /// Tools for dealing with agents inventory
997 /// </summary>
998 [Serializable()]
999 public class InventoryManager
1000 {
1001 /// <summary>Used for converting shadow_id to asset_id</summary>
1002 public static readonly UUID MAGIC_ID = new UUID("3c115e51-04f4-523c-9fa6-98aff1034730");
1003  
1004 protected struct InventorySearch
1005 {
1006 public UUID Folder;
1007 public UUID Owner;
1008 public string[] Path;
1009 public int Level;
1010 }
1011  
1012 #region Delegates
1013  
1014 /// <summary>
1015 /// Callback for inventory item creation finishing
1016 /// </summary>
1017 /// <param name="success">Whether the request to create an inventory
1018 /// item succeeded or not</param>
1019 /// <param name="item">Inventory item being created. If success is
1020 /// false this will be null</param>
1021 public delegate void ItemCreatedCallback(bool success, InventoryItem item);
1022  
1023 /// <summary>
1024 /// Callback for an inventory item being create from an uploaded asset
1025 /// </summary>
1026 /// <param name="success">true if inventory item creation was successful</param>
1027 /// <param name="status"></param>
1028 /// <param name="itemID"></param>
1029 /// <param name="assetID"></param>
1030 public delegate void ItemCreatedFromAssetCallback(bool success, string status, UUID itemID, UUID assetID);
1031  
1032 /// <summary>
1033 ///
1034 /// </summary>
1035 /// <param name="item"></param>
1036 public delegate void ItemCopiedCallback(InventoryBase item);
1037  
1038 /// <summary>The event subscribers, null of no subscribers</summary>
1039 private EventHandler<ItemReceivedEventArgs> m_ItemReceived;
1040  
1041 ///<summary>Raises the ItemReceived Event</summary>
1042 /// <param name="e">A ItemReceivedEventArgs object containing
1043 /// the data sent from the simulator</param>
1044 protected virtual void OnItemReceived(ItemReceivedEventArgs e)
1045 {
1046 EventHandler<ItemReceivedEventArgs> handler = m_ItemReceived;
1047 if (handler != null)
1048 handler(this, e);
1049 }
1050  
1051 /// <summary>Thread sync lock object</summary>
1052 private readonly object m_ItemReceivedLock = new object();
1053  
1054 /// <summary>Raised when the simulator sends us data containing
1055 /// ...</summary>
1056 public event EventHandler<ItemReceivedEventArgs> ItemReceived
1057 {
1058 add { lock (m_ItemReceivedLock) { m_ItemReceived += value; } }
1059 remove { lock (m_ItemReceivedLock) { m_ItemReceived -= value; } }
1060 }
1061  
1062  
1063 /// <summary>The event subscribers, null of no subscribers</summary>
1064 private EventHandler<FolderUpdatedEventArgs> m_FolderUpdated;
1065  
1066 ///<summary>Raises the FolderUpdated Event</summary>
1067 /// <param name="e">A FolderUpdatedEventArgs object containing
1068 /// the data sent from the simulator</param>
1069 protected virtual void OnFolderUpdated(FolderUpdatedEventArgs e)
1070 {
1071 EventHandler<FolderUpdatedEventArgs> handler = m_FolderUpdated;
1072 if (handler != null)
1073 handler(this, e);
1074 }
1075  
1076 /// <summary>Thread sync lock object</summary>
1077 private readonly object m_FolderUpdatedLock = new object();
1078  
1079 /// <summary>Raised when the simulator sends us data containing
1080 /// ...</summary>
1081 public event EventHandler<FolderUpdatedEventArgs> FolderUpdated
1082 {
1083 add { lock (m_FolderUpdatedLock) { m_FolderUpdated += value; } }
1084 remove { lock (m_FolderUpdatedLock) { m_FolderUpdated -= value; } }
1085 }
1086  
1087  
1088 /// <summary>The event subscribers, null of no subscribers</summary>
1089 private EventHandler<InventoryObjectOfferedEventArgs> m_InventoryObjectOffered;
1090  
1091 ///<summary>Raises the InventoryObjectOffered Event</summary>
1092 /// <param name="e">A InventoryObjectOfferedEventArgs object containing
1093 /// the data sent from the simulator</param>
1094 protected virtual void OnInventoryObjectOffered(InventoryObjectOfferedEventArgs e)
1095 {
1096 EventHandler<InventoryObjectOfferedEventArgs> handler = m_InventoryObjectOffered;
1097 if (handler != null)
1098 handler(this, e);
1099 }
1100  
1101 /// <summary>Thread sync lock object</summary>
1102 private readonly object m_InventoryObjectOfferedLock = new object();
1103  
1104 /// <summary>Raised when the simulator sends us data containing
1105 /// an inventory object sent by another avatar or primitive</summary>
1106 public event EventHandler<InventoryObjectOfferedEventArgs> InventoryObjectOffered
1107 {
1108 add { lock (m_InventoryObjectOfferedLock) { m_InventoryObjectOffered += value; } }
1109 remove { lock (m_InventoryObjectOfferedLock) { m_InventoryObjectOffered -= value; } }
1110 }
1111  
1112 /// <summary>The event subscribers, null of no subscribers</summary>
1113 private EventHandler<TaskItemReceivedEventArgs> m_TaskItemReceived;
1114  
1115 ///<summary>Raises the TaskItemReceived Event</summary>
1116 /// <param name="e">A TaskItemReceivedEventArgs object containing
1117 /// the data sent from the simulator</param>
1118 protected virtual void OnTaskItemReceived(TaskItemReceivedEventArgs e)
1119 {
1120 EventHandler<TaskItemReceivedEventArgs> handler = m_TaskItemReceived;
1121 if (handler != null)
1122 handler(this, e);
1123 }
1124  
1125 /// <summary>Thread sync lock object</summary>
1126 private readonly object m_TaskItemReceivedLock = new object();
1127  
1128 /// <summary>Raised when the simulator sends us data containing
1129 /// ...</summary>
1130 public event EventHandler<TaskItemReceivedEventArgs> TaskItemReceived
1131 {
1132 add { lock (m_TaskItemReceivedLock) { m_TaskItemReceived += value; } }
1133 remove { lock (m_TaskItemReceivedLock) { m_TaskItemReceived -= value; } }
1134 }
1135  
1136  
1137 /// <summary>The event subscribers, null of no subscribers</summary>
1138 private EventHandler<FindObjectByPathReplyEventArgs> m_FindObjectByPathReply;
1139  
1140 ///<summary>Raises the FindObjectByPath Event</summary>
1141 /// <param name="e">A FindObjectByPathEventArgs object containing
1142 /// the data sent from the simulator</param>
1143 protected virtual void OnFindObjectByPathReply(FindObjectByPathReplyEventArgs e)
1144 {
1145 EventHandler<FindObjectByPathReplyEventArgs> handler = m_FindObjectByPathReply;
1146 if (handler != null)
1147 handler(this, e);
1148 }
1149  
1150 /// <summary>Thread sync lock object</summary>
1151 private readonly object m_FindObjectByPathReplyLock = new object();
1152  
1153 /// <summary>Raised when the simulator sends us data containing
1154 /// ...</summary>
1155 public event EventHandler<FindObjectByPathReplyEventArgs> FindObjectByPathReply
1156 {
1157 add { lock (m_FindObjectByPathReplyLock) { m_FindObjectByPathReply += value; } }
1158 remove { lock (m_FindObjectByPathReplyLock) { m_FindObjectByPathReply -= value; } }
1159 }
1160  
1161  
1162 /// <summary>The event subscribers, null of no subscribers</summary>
1163 private EventHandler<TaskInventoryReplyEventArgs> m_TaskInventoryReply;
1164  
1165 ///<summary>Raises the TaskInventoryReply Event</summary>
1166 /// <param name="e">A TaskInventoryReplyEventArgs object containing
1167 /// the data sent from the simulator</param>
1168 protected virtual void OnTaskInventoryReply(TaskInventoryReplyEventArgs e)
1169 {
1170 EventHandler<TaskInventoryReplyEventArgs> handler = m_TaskInventoryReply;
1171 if (handler != null)
1172 handler(this, e);
1173 }
1174  
1175 /// <summary>Thread sync lock object</summary>
1176 private readonly object m_TaskInventoryReplyLock = new object();
1177  
1178 /// <summary>Raised when the simulator sends us data containing
1179 /// ...</summary>
1180 public event EventHandler<TaskInventoryReplyEventArgs> TaskInventoryReply
1181 {
1182 add { lock (m_TaskInventoryReplyLock) { m_TaskInventoryReply += value; } }
1183 remove { lock (m_TaskInventoryReplyLock) { m_TaskInventoryReply -= value; } }
1184 }
1185  
1186 /// <summary>
1187 /// Reply received when uploading an inventory asset
1188 /// </summary>
1189 /// <param name="success">Has upload been successful</param>
1190 /// <param name="status">Error message if upload failed</param>
1191 /// <param name="itemID">Inventory asset UUID</param>
1192 /// <param name="assetID">New asset UUID</param>
1193 public delegate void InventoryUploadedAssetCallback(bool success, string status, UUID itemID, UUID assetID);
1194  
1195  
1196 /// <summary>The event subscribers, null of no subscribers</summary>
1197 private EventHandler<SaveAssetToInventoryEventArgs> m_SaveAssetToInventory;
1198  
1199 ///<summary>Raises the SaveAssetToInventory Event</summary>
1200 /// <param name="e">A SaveAssetToInventoryEventArgs object containing
1201 /// the data sent from the simulator</param>
1202 protected virtual void OnSaveAssetToInventory(SaveAssetToInventoryEventArgs e)
1203 {
1204 EventHandler<SaveAssetToInventoryEventArgs> handler = m_SaveAssetToInventory;
1205 if (handler != null)
1206 handler(this, e);
1207 }
1208  
1209 /// <summary>Thread sync lock object</summary>
1210 private readonly object m_SaveAssetToInventoryLock = new object();
1211  
1212 /// <summary>Raised when the simulator sends us data containing
1213 /// ...</summary>
1214 public event EventHandler<SaveAssetToInventoryEventArgs> SaveAssetToInventory
1215 {
1216 add { lock (m_SaveAssetToInventoryLock) { m_SaveAssetToInventory += value; } }
1217 remove { lock (m_SaveAssetToInventoryLock) { m_SaveAssetToInventory -= value; } }
1218 }
1219  
1220 /// <summary>
1221 /// Delegate that is invoked when script upload is completed
1222 /// </summary>
1223 /// <param name="uploadSuccess">Has upload succeded (note, there still might be compile errors)</param>
1224 /// <param name="uploadStatus">Upload status message</param>
1225 /// <param name="compileSuccess">Is compilation successful</param>
1226 /// <param name="compileMessages">If compilation failed, list of error messages, null on compilation success</param>
1227 /// <param name="itemID">Script inventory UUID</param>
1228 /// <param name="assetID">Script's new asset UUID</param>
1229 public delegate void ScriptUpdatedCallback(bool uploadSuccess, string uploadStatus, bool compileSuccess, List<string> compileMessages, UUID itemID, UUID assetID);
1230  
1231 /// <summary>The event subscribers, null of no subscribers</summary>
1232 private EventHandler<ScriptRunningReplyEventArgs> m_ScriptRunningReply;
1233  
1234 ///<summary>Raises the ScriptRunningReply Event</summary>
1235 /// <param name="e">A ScriptRunningReplyEventArgs object containing
1236 /// the data sent from the simulator</param>
1237 protected virtual void OnScriptRunningReply(ScriptRunningReplyEventArgs e)
1238 {
1239 EventHandler<ScriptRunningReplyEventArgs> handler = m_ScriptRunningReply;
1240 if (handler != null)
1241 handler(this, e);
1242 }
1243  
1244 /// <summary>Thread sync lock object</summary>
1245 private readonly object m_ScriptRunningReplyLock = new object();
1246  
1247 /// <summary>Raised when the simulator sends us data containing
1248 /// ...</summary>
1249 public event EventHandler<ScriptRunningReplyEventArgs> ScriptRunningReply
1250 {
1251 add { lock (m_ScriptRunningReplyLock) { m_ScriptRunningReply += value; } }
1252 remove { lock (m_ScriptRunningReplyLock) { m_ScriptRunningReply -= value; } }
1253 }
1254 #endregion Delegates
1255  
1256 #region String Arrays
1257  
1258 /// <summary>Partial mapping of AssetTypes to folder names</summary>
1259 private static readonly string[] _NewFolderNames = new string[]
1260 {
1261 "Textures",
1262 "Sounds",
1263 "Calling Cards",
1264 "Landmarks",
1265 "Scripts",
1266 "Clothing",
1267 "Objects",
1268 "Notecards",
1269 "New Folder",
1270 "Inventory",
1271 "Scripts",
1272 "Scripts",
1273 "Uncompressed Images",
1274 "Body Parts",
1275 "Trash",
1276 "Photo Album",
1277 "Lost And Found",
1278 "Uncompressed Sounds",
1279 "Uncompressed Images",
1280 "Uncompressed Images",
1281 "Animations",
1282 "Gestures"
1283 };
1284  
1285 #endregion String Arrays
1286  
1287 private GridClient Client;
1288 private Inventory _Store;
1289 //private Random _RandNumbers = new Random();
1290 private object _CallbacksLock = new object();
1291 private uint _CallbackPos;
1292 private Dictionary<uint, ItemCreatedCallback> _ItemCreatedCallbacks = new Dictionary<uint, ItemCreatedCallback>();
1293 private Dictionary<uint, ItemCopiedCallback> _ItemCopiedCallbacks = new Dictionary<uint, ItemCopiedCallback>();
1294 private Dictionary<uint, InventoryType> _ItemInventoryTypeRequest = new Dictionary<uint, InventoryType>();
1295 private List<InventorySearch> _Searches = new List<InventorySearch>();
1296  
1297 #region Properties
1298  
1299 /// <summary>
1300 /// Get this agents Inventory data
1301 /// </summary>
1302 public Inventory Store { get { return _Store; } }
1303  
1304 #endregion Properties
1305  
1306 /// <summary>
1307 /// Default constructor
1308 /// </summary>
1309 /// <param name="client">Reference to the GridClient object</param>
1310 public InventoryManager(GridClient client)
1311 {
1312 Client = client;
1313  
1314 Client.Network.RegisterCallback(PacketType.UpdateCreateInventoryItem, UpdateCreateInventoryItemHandler);
1315 Client.Network.RegisterCallback(PacketType.SaveAssetIntoInventory, SaveAssetIntoInventoryHandler);
1316 Client.Network.RegisterCallback(PacketType.BulkUpdateInventory, BulkUpdateInventoryHandler);
1317 Client.Network.RegisterEventCallback("BulkUpdateInventory", new Caps.EventQueueCallback(BulkUpdateInventoryCapHandler));
1318 Client.Network.RegisterCallback(PacketType.MoveInventoryItem, MoveInventoryItemHandler);
1319 Client.Network.RegisterCallback(PacketType.InventoryDescendents, InventoryDescendentsHandler);
1320 Client.Network.RegisterCallback(PacketType.FetchInventoryReply, FetchInventoryReplyHandler);
1321 Client.Network.RegisterCallback(PacketType.ReplyTaskInventory, ReplyTaskInventoryHandler);
1322 Client.Network.RegisterEventCallback("ScriptRunningReply", new Caps.EventQueueCallback(ScriptRunningReplyMessageHandler));
1323  
1324 // Watch for inventory given to us through instant message
1325 Client.Self.IM += Self_IM;
1326  
1327 // Register extra parameters with login and parse the inventory data that comes back
1328 Client.Network.RegisterLoginResponseCallback(
1329 new NetworkManager.LoginResponseCallback(Network_OnLoginResponse),
1330 new string[] {
1331 "inventory-root", "inventory-skeleton", "inventory-lib-root",
1332 "inventory-lib-owner", "inventory-skel-lib"});
1333 }
1334  
1335  
1336 #region Fetch
1337  
1338 /// <summary>
1339 /// Fetch an inventory item from the dataserver
1340 /// </summary>
1341 /// <param name="itemID">The items <seealso cref="UUID"/></param>
1342 /// <param name="ownerID">The item Owners <seealso cref="OpenMetaverse.UUID"/></param>
1343 /// <param name="timeoutMS">a integer representing the number of milliseconds to wait for results</param>
1344 /// <returns>An <seealso cref="InventoryItem"/> object on success, or null if no item was found</returns>
1345 /// <remarks>Items will also be sent to the <seealso cref="InventoryManager.OnItemReceived"/> event</remarks>
1346 public InventoryItem FetchItem(UUID itemID, UUID ownerID, int timeoutMS)
1347 {
1348 AutoResetEvent fetchEvent = new AutoResetEvent(false);
1349 InventoryItem fetchedItem = null;
1350  
1351 EventHandler<ItemReceivedEventArgs> callback =
1352 delegate(object sender, ItemReceivedEventArgs e)
1353 {
1354 if (e.Item.UUID == itemID)
1355 {
1356 fetchedItem = e.Item;
1357 fetchEvent.Set();
1358 }
1359 };
1360  
1361 ItemReceived += callback;
1362 RequestFetchInventory(itemID, ownerID);
1363  
1364 fetchEvent.WaitOne(timeoutMS, false);
1365 ItemReceived -= callback;
1366  
1367 return fetchedItem;
1368 }
1369  
1370 /// <summary>
1371 /// Request A single inventory item
1372 /// </summary>
1373 /// <param name="itemID">The items <seealso cref="OpenMetaverse.UUID"/></param>
1374 /// <param name="ownerID">The item Owners <seealso cref="OpenMetaverse.UUID"/></param>
1375 /// <seealso cref="InventoryManager.OnItemReceived"/>
1376 public void RequestFetchInventory(UUID itemID, UUID ownerID)
1377 {
1378 RequestFetchInventory(new List<UUID>(1) { itemID }, new List<UUID>(1) { ownerID });
1379 }
1380  
1381 /// <summary>
1382 /// Request inventory items
1383 /// </summary>
1384 /// <param name="itemIDs">Inventory items to request</param>
1385 /// <param name="ownerIDs">Owners of the inventory items</param>
1386 /// <seealso cref="InventoryManager.OnItemReceived"/>
1387 public void RequestFetchInventory(List<UUID> itemIDs, List<UUID> ownerIDs)
1388 {
1389 if (itemIDs.Count != ownerIDs.Count)
1390 throw new ArgumentException("itemIDs and ownerIDs must contain the same number of entries");
1391  
1392 if (Client.Settings.HTTP_INVENTORY &&
1393 Client.Network.CurrentSim.Caps != null &&
1394 Client.Network.CurrentSim.Caps.CapabilityURI("FetchInventory2") != null)
1395 {
1396 RequestFetchInventoryCap(itemIDs, ownerIDs);
1397 return;
1398 }
1399  
1400  
1401 FetchInventoryPacket fetch = new FetchInventoryPacket();
1402 fetch.AgentData = new FetchInventoryPacket.AgentDataBlock();
1403 fetch.AgentData.AgentID = Client.Self.AgentID;
1404 fetch.AgentData.SessionID = Client.Self.SessionID;
1405  
1406 fetch.InventoryData = new FetchInventoryPacket.InventoryDataBlock[itemIDs.Count];
1407 for (int i = 0; i < itemIDs.Count; i++)
1408 {
1409 fetch.InventoryData[i] = new FetchInventoryPacket.InventoryDataBlock();
1410 fetch.InventoryData[i].ItemID = itemIDs[i];
1411 fetch.InventoryData[i].OwnerID = ownerIDs[i];
1412 }
1413  
1414 Client.Network.SendPacket(fetch);
1415 }
1416  
1417 /// <summary>
1418 /// Request inventory items via Capabilities
1419 /// </summary>
1420 /// <param name="itemIDs">Inventory items to request</param>
1421 /// <param name="ownerIDs">Owners of the inventory items</param>
1422 /// <seealso cref="InventoryManager.OnItemReceived"/>
1423 private void RequestFetchInventoryCap(List<UUID> itemIDs, List<UUID> ownerIDs)
1424 {
1425 if (itemIDs.Count != ownerIDs.Count)
1426 throw new ArgumentException("itemIDs and ownerIDs must contain the same number of entries");
1427  
1428 if (Client.Settings.HTTP_INVENTORY &&
1429 Client.Network.CurrentSim.Caps != null &&
1430 Client.Network.CurrentSim.Caps.CapabilityURI("FetchInventory2") != null)
1431 {
1432 Uri url = Client.Network.CurrentSim.Caps.CapabilityURI("FetchInventory2");
1433 CapsClient request = new CapsClient(url);
1434  
1435 request.OnComplete += (client, result, error) =>
1436 {
1437 if (error == null)
1438 {
1439 try
1440 {
1441 OSDMap res = (OSDMap)result;
1442 OSDArray itemsOSD = (OSDArray)res["items"];
1443  
1444 for (int i = 0; i < itemsOSD.Count; i++)
1445 {
1446 InventoryItem item = InventoryItem.FromOSD(itemsOSD[i]);
1447 _Store[item.UUID] = item;
1448 OnItemReceived(new ItemReceivedEventArgs(item));
1449 }
1450 }
1451 catch (Exception ex)
1452 {
1453 Logger.Log("Failed getting data from FetchInventory2 capability.", Helpers.LogLevel.Error, Client, ex);
1454 }
1455 }
1456 };
1457  
1458 OSDMap OSDRequest = new OSDMap();
1459 OSDRequest["agent_id"] = Client.Self.AgentID;
1460  
1461 OSDArray items = new OSDArray(itemIDs.Count);
1462 for (int i = 0; i < itemIDs.Count; i++)
1463 {
1464 OSDMap item = new OSDMap(2);
1465 item["item_id"] = itemIDs[i];
1466 item["owner_id"] = ownerIDs[i];
1467 items.Add(item);
1468 }
1469  
1470 OSDRequest["items"] = items;
1471  
1472 request.BeginGetResponse(OSDRequest, OSDFormat.Xml, Client.Settings.CAPS_TIMEOUT);
1473 }
1474 }
1475  
1476 /// <summary>
1477 /// Get contents of a folder
1478 /// </summary>
1479 /// <param name="folder">The <seealso cref="UUID"/> of the folder to search</param>
1480 /// <param name="owner">The <seealso cref="UUID"/> of the folders owner</param>
1481 /// <param name="folders">true to retrieve folders</param>
1482 /// <param name="items">true to retrieve items</param>
1483 /// <param name="order">sort order to return results in</param>
1484 /// <param name="timeoutMS">a integer representing the number of milliseconds to wait for results</param>
1485 /// <returns>A list of inventory items matching search criteria within folder</returns>
1486 /// <seealso cref="InventoryManager.RequestFolderContents"/>
1487 /// <remarks>InventoryFolder.DescendentCount will only be accurate if both folders and items are
1488 /// requested</remarks>
1489 public List<InventoryBase> FolderContents(UUID folder, UUID owner, bool folders, bool items,
1490 InventorySortOrder order, int timeoutMS)
1491 {
1492 List<InventoryBase> objects = null;
1493 AutoResetEvent fetchEvent = new AutoResetEvent(false);
1494  
1495 EventHandler<FolderUpdatedEventArgs> callback =
1496 delegate(object sender, FolderUpdatedEventArgs e)
1497 {
1498 if (e.FolderID == folder
1499 && _Store[folder] is InventoryFolder)
1500 {
1501 // InventoryDescendentsHandler only stores DescendendCount if both folders and items are fetched.
1502 if (_Store.GetContents(folder).Count >= ((InventoryFolder)_Store[folder]).DescendentCount)
1503 {
1504  
1505 fetchEvent.Set();
1506 }
1507 }
1508 else
1509 {
1510 fetchEvent.Set();
1511 }
1512 };
1513  
1514 FolderUpdated += callback;
1515  
1516 RequestFolderContents(folder, owner, folders, items, order);
1517 if (fetchEvent.WaitOne(timeoutMS, false))
1518 objects = _Store.GetContents(folder);
1519  
1520 FolderUpdated -= callback;
1521  
1522 return objects;
1523 }
1524  
1525 /// <summary>
1526 /// Request the contents of an inventory folder
1527 /// </summary>
1528 /// <param name="folder">The folder to search</param>
1529 /// <param name="owner">The folder owners <seealso cref="UUID"/></param>
1530 /// <param name="folders">true to return <seealso cref="InventoryManager.InventoryFolder"/>s contained in folder</param>
1531 /// <param name="items">true to return <seealso cref="InventoryManager.InventoryItem"/>s containd in folder</param>
1532 /// <param name="order">the sort order to return items in</param>
1533 /// <seealso cref="InventoryManager.FolderContents"/>
1534 public void RequestFolderContents(UUID folder, UUID owner, bool folders, bool items,
1535 InventorySortOrder order)
1536 {
1537 string cap = owner == Client.Self.AgentID ? "FetchInventoryDescendents2" : "FetchLibDescendents2";
1538  
1539 if (Client.Settings.HTTP_INVENTORY &&
1540 Client.Network.CurrentSim.Caps != null &&
1541 Client.Network.CurrentSim.Caps.CapabilityURI(cap) != null)
1542 {
1543 RequestFolderContentsCap(folder, owner, folders, items, order);
1544 return;
1545 }
1546  
1547 FetchInventoryDescendentsPacket fetch = new FetchInventoryDescendentsPacket();
1548 fetch.AgentData.AgentID = Client.Self.AgentID;
1549 fetch.AgentData.SessionID = Client.Self.SessionID;
1550  
1551 fetch.InventoryData.FetchFolders = folders;
1552 fetch.InventoryData.FetchItems = items;
1553 fetch.InventoryData.FolderID = folder;
1554 fetch.InventoryData.OwnerID = owner;
1555 fetch.InventoryData.SortOrder = (int)order;
1556  
1557 Client.Network.SendPacket(fetch);
1558 }
1559  
1560 /// <summary>
1561 /// Request the contents of an inventory folder using HTTP capabilities
1562 /// </summary>
1563 /// <param name="folderID">The folder to search</param>
1564 /// <param name="ownerID">The folder owners <seealso cref="UUID"/></param>
1565 /// <param name="fetchFolders">true to return <seealso cref="InventoryManager.InventoryFolder"/>s contained in folder</param>
1566 /// <param name="fetchItems">true to return <seealso cref="InventoryManager.InventoryItem"/>s containd in folder</param>
1567 /// <param name="order">the sort order to return items in</param>
1568 /// <seealso cref="InventoryManager.FolderContents"/>
1569 public void RequestFolderContentsCap(UUID folderID, UUID ownerID, bool fetchFolders, bool fetchItems,
1570 InventorySortOrder order)
1571 {
1572 Uri url = null;
1573 string cap = ownerID == Client.Self.AgentID ? "FetchInventoryDescendents2" : "FetchLibDescendents2";
1574 if (Client.Network.CurrentSim.Caps == null ||
1575 null == (url = Client.Network.CurrentSim.Caps.CapabilityURI(cap)))
1576 {
1577 Logger.Log(cap + " capability not available in the current sim", Helpers.LogLevel.Warning, Client);
1578 OnFolderUpdated(new FolderUpdatedEventArgs(folderID, false));
1579 return;
1580 }
1581  
1582 InventoryFolder folder = new InventoryFolder(folderID);
1583 folder.OwnerID = ownerID;
1584 folder.UUID = folderID;
1585 RequestFolderContentsCap(new List<InventoryFolder>() { folder }, url, fetchFolders, fetchItems, order);
1586 }
1587  
1588 public void RequestFolderContentsCap(List<InventoryFolder> batch, Uri url, bool fetchFolders, bool fetchItems, InventorySortOrder order)
1589 {
1590  
1591 try
1592 {
1593 CapsClient request = new CapsClient(url);
1594 request.OnComplete += (client, result, error) =>
1595 {
1596 try
1597 {
1598 if (error != null)
1599 {
1600 throw error;
1601 }
1602  
1603 OSDMap resultMap = ((OSDMap)result);
1604 if (resultMap.ContainsKey("folders"))
1605 {
1606 OSDArray fetchedFolders = (OSDArray)resultMap["folders"];
1607 for (int fetchedFolderNr = 0; fetchedFolderNr < fetchedFolders.Count; fetchedFolderNr++)
1608 {
1609 OSDMap res = (OSDMap)fetchedFolders[fetchedFolderNr];
1610 InventoryFolder fetchedFolder = null;
1611  
1612 if (_Store.Contains(res["folder_id"])
1613 && _Store[res["folder_id"]] is InventoryFolder)
1614 {
1615 fetchedFolder = (InventoryFolder)_Store[res["folder_id"]];
1616 }
1617 else
1618 {
1619 fetchedFolder = new InventoryFolder(res["folder_id"]);
1620 _Store[res["folder_id"]] = fetchedFolder;
1621 }
1622 fetchedFolder.DescendentCount = res["descendents"];
1623 fetchedFolder.Version = res["version"];
1624 fetchedFolder.OwnerID = res["owner_id"];
1625 _Store.GetNodeFor(fetchedFolder.UUID).NeedsUpdate = false;
1626  
1627 // Do we have any descendants
1628 if (fetchedFolder.DescendentCount > 0)
1629 {
1630 // Fetch descendent folders
1631 if (res["categories"] is OSDArray)
1632 {
1633 OSDArray folders = (OSDArray)res["categories"];
1634 for (int i = 0; i < folders.Count; i++)
1635 {
1636 OSDMap descFolder = (OSDMap)folders[i];
1637 InventoryFolder folder;
1638 UUID folderID = descFolder.ContainsKey("category_id") ? descFolder["category_id"] : descFolder["folder_id"];
1639 if (!_Store.Contains(folderID))
1640 {
1641 folder = new InventoryFolder(folderID);
1642 folder.ParentUUID = descFolder["parent_id"];
1643 _Store[folderID] = folder;
1644 }
1645 else
1646 {
1647 folder = (InventoryFolder)_Store[folderID];
1648 }
1649  
1650 folder.OwnerID = descFolder["agent_id"];
1651 folder.ParentUUID = descFolder["parent_id"];
1652 folder.Name = descFolder["name"];
1653 folder.Version = descFolder["version"];
1654 folder.PreferredType = (AssetType)(int)descFolder["type_default"];
1655 }
1656  
1657 // Fetch descendent items
1658 OSDArray items = (OSDArray)res["items"];
1659 for (int i = 0; i < items.Count; i++)
1660 {
1661 InventoryItem item = InventoryItem.FromOSD(items[i]);
1662 _Store[item.UUID] = item;
1663 }
1664 }
1665 }
1666  
1667 OnFolderUpdated(new FolderUpdatedEventArgs(res["folder_id"], true));
1668 }
1669 }
1670 }
1671 catch (Exception exc)
1672 {
1673 Logger.Log(string.Format("Failed to fetch inventory descendants: {0}\n{1}", exc.Message, exc.StackTrace.ToString()), Helpers.LogLevel.Warning, Client);
1674 foreach (var f in batch)
1675 {
1676 OnFolderUpdated(new FolderUpdatedEventArgs(f.UUID, false));
1677 }
1678 return;
1679 }
1680  
1681 };
1682  
1683 // Construct request
1684 OSDArray requestedFolders = new OSDArray(1);
1685 foreach (var f in batch)
1686 {
1687 OSDMap requestedFolder = new OSDMap(1);
1688 requestedFolder["folder_id"] = f.UUID;
1689 requestedFolder["owner_id"] = f.OwnerID;
1690 requestedFolder["fetch_folders"] = fetchFolders;
1691 requestedFolder["fetch_items"] = fetchItems;
1692 requestedFolder["sort_order"] = (int)order;
1693  
1694 requestedFolders.Add(requestedFolder);
1695 }
1696 OSDMap req = new OSDMap(1);
1697 req["folders"] = requestedFolders;
1698  
1699 request.BeginGetResponse(req, OSDFormat.Xml, Client.Settings.CAPS_TIMEOUT);
1700 }
1701 catch (Exception ex)
1702 {
1703 Logger.Log(string.Format("Failed to fetch inventory descendants: {0}\n{1}", ex.Message, ex.StackTrace.ToString()), Helpers.LogLevel.Warning, Client);
1704 foreach (var f in batch)
1705 {
1706 OnFolderUpdated(new FolderUpdatedEventArgs(f.UUID, false));
1707 }
1708 return;
1709 }
1710 }
1711  
1712 #endregion Fetch
1713  
1714 #region Find
1715  
1716 /// <summary>
1717 /// Returns the UUID of the folder (category) that defaults to
1718 /// containing 'type'. The folder is not necessarily only for that
1719 /// type
1720 /// </summary>
1721 /// <remarks>This will return the root folder if one does not exist</remarks>
1722 /// <param name="type"></param>
1723 /// <returns>The UUID of the desired folder if found, the UUID of the RootFolder
1724 /// if not found, or UUID.Zero on failure</returns>
1725 public UUID FindFolderForType(AssetType type)
1726 {
1727 if (_Store == null)
1728 {
1729 Logger.Log("Inventory is null, FindFolderForType() lookup cannot continue",
1730 Helpers.LogLevel.Error, Client);
1731 return UUID.Zero;
1732 }
1733  
1734 // Folders go in the root
1735 if (type == AssetType.Folder)
1736 return _Store.RootFolder.UUID;
1737  
1738 // Loop through each top-level directory and check if PreferredType
1739 // matches the requested type
1740 List<InventoryBase> contents = _Store.GetContents(_Store.RootFolder.UUID);
1741 foreach (InventoryBase inv in contents)
1742 {
1743 if (inv is InventoryFolder)
1744 {
1745 InventoryFolder folder = inv as InventoryFolder;
1746  
1747 if (folder.PreferredType == type)
1748 return folder.UUID;
1749 }
1750 }
1751  
1752 // No match found, return Root Folder ID
1753 return _Store.RootFolder.UUID;
1754 }
1755  
1756 /// <summary>
1757 /// Find an object in inventory using a specific path to search
1758 /// </summary>
1759 /// <param name="baseFolder">The folder to begin the search in</param>
1760 /// <param name="inventoryOwner">The object owners <seealso cref="UUID"/></param>
1761 /// <param name="path">A string path to search</param>
1762 /// <param name="timeoutMS">milliseconds to wait for a reply</param>
1763 /// <returns>Found items <seealso cref="UUID"/> or <seealso cref="UUID.Zero"/> if
1764 /// timeout occurs or item is not found</returns>
1765 public UUID FindObjectByPath(UUID baseFolder, UUID inventoryOwner, string path, int timeoutMS)
1766 {
1767 AutoResetEvent findEvent = new AutoResetEvent(false);
1768 UUID foundItem = UUID.Zero;
1769  
1770 EventHandler<FindObjectByPathReplyEventArgs> callback =
1771 delegate(object sender, FindObjectByPathReplyEventArgs e)
1772 {
1773 if (e.Path == path)
1774 {
1775 foundItem = e.InventoryObjectID;
1776 findEvent.Set();
1777 }
1778 };
1779  
1780 FindObjectByPathReply += callback;
1781  
1782 RequestFindObjectByPath(baseFolder, inventoryOwner, path);
1783 findEvent.WaitOne(timeoutMS, false);
1784  
1785 FindObjectByPathReply -= callback;
1786  
1787 return foundItem;
1788 }
1789  
1790 /// <summary>
1791 /// Find inventory items by path
1792 /// </summary>
1793 /// <param name="baseFolder">The folder to begin the search in</param>
1794 /// <param name="inventoryOwner">The object owners <seealso cref="UUID"/></param>
1795 /// <param name="path">A string path to search, folders/objects separated by a '/'</param>
1796 /// <remarks>Results are sent to the <seealso cref="InventoryManager.OnFindObjectByPath"/> event</remarks>
1797 public void RequestFindObjectByPath(UUID baseFolder, UUID inventoryOwner, string path)
1798 {
1799 if (path == null || path.Length == 0)
1800 throw new ArgumentException("Empty path is not supported");
1801  
1802 // Store this search
1803 InventorySearch search;
1804 search.Folder = baseFolder;
1805 search.Owner = inventoryOwner;
1806 search.Path = path.Split('/');
1807 search.Level = 0;
1808 lock (_Searches) _Searches.Add(search);
1809  
1810 // Start the search
1811 RequestFolderContents(baseFolder, inventoryOwner, true, true, InventorySortOrder.ByName);
1812 }
1813  
1814 /// <summary>
1815 /// Search inventory Store object for an item or folder
1816 /// </summary>
1817 /// <param name="baseFolder">The folder to begin the search in</param>
1818 /// <param name="path">An array which creates a path to search</param>
1819 /// <param name="level">Number of levels below baseFolder to conduct searches</param>
1820 /// <param name="firstOnly">if True, will stop searching after first match is found</param>
1821 /// <returns>A list of inventory items found</returns>
1822 public List<InventoryBase> LocalFind(UUID baseFolder, string[] path, int level, bool firstOnly)
1823 {
1824 List<InventoryBase> objects = new List<InventoryBase>();
1825 //List<InventoryFolder> folders = new List<InventoryFolder>();
1826 List<InventoryBase> contents = _Store.GetContents(baseFolder);
1827  
1828 foreach (InventoryBase inv in contents)
1829 {
1830 if (inv.Name.CompareTo(path[level]) == 0)
1831 {
1832 if (level == path.Length - 1)
1833 {
1834 objects.Add(inv);
1835 if (firstOnly) return objects;
1836 }
1837 else if (inv is InventoryFolder)
1838 objects.AddRange(LocalFind(inv.UUID, path, level + 1, firstOnly));
1839 }
1840 }
1841  
1842 return objects;
1843 }
1844  
1845 #endregion Find
1846  
1847 #region Move/Rename
1848  
1849 /// <summary>
1850 /// Move an inventory item or folder to a new location
1851 /// </summary>
1852 /// <param name="item">The <seealso cref="T:InventoryBase"/> item or folder to move</param>
1853 /// <param name="newParent">The <seealso cref="T:InventoryFolder"/> to move item or folder to</param>
1854 public void Move(InventoryBase item, InventoryFolder newParent)
1855 {
1856 if (item is InventoryFolder)
1857 MoveFolder(item.UUID, newParent.UUID);
1858 else
1859 MoveItem(item.UUID, newParent.UUID);
1860 }
1861  
1862 /// <summary>
1863 /// Move an inventory item or folder to a new location and change its name
1864 /// </summary>
1865 /// <param name="item">The <seealso cref="T:InventoryBase"/> item or folder to move</param>
1866 /// <param name="newParent">The <seealso cref="T:InventoryFolder"/> to move item or folder to</param>
1867 /// <param name="newName">The name to change the item or folder to</param>
1868 public void Move(InventoryBase item, InventoryFolder newParent, string newName)
1869 {
1870 if (item is InventoryFolder)
1871 MoveFolder(item.UUID, newParent.UUID, newName);
1872 else
1873 MoveItem(item.UUID, newParent.UUID, newName);
1874 }
1875  
1876 /// <summary>
1877 /// Move and rename a folder
1878 /// </summary>
1879 /// <param name="folderID">The source folders <seealso cref="UUID"/></param>
1880 /// <param name="newparentID">The destination folders <seealso cref="UUID"/></param>
1881 /// <param name="newName">The name to change the folder to</param>
1882 public void MoveFolder(UUID folderID, UUID newparentID, string newName)
1883 {
1884 UpdateFolderProperties(folderID, newparentID, newName, AssetType.Unknown);
1885 }
1886  
1887 /// <summary>
1888 /// Update folder properties
1889 /// </summary>
1890 /// <param name="folderID"><seealso cref="UUID"/> of the folder to update</param>
1891 /// <param name="parentID">Sets folder's parent to <seealso cref="UUID"/></param>
1892 /// <param name="name">Folder name</param>
1893 /// <param name="type">Folder type</param>
1894 public void UpdateFolderProperties(UUID folderID, UUID parentID, string name, AssetType type)
1895 {
1896 lock (Store)
1897 {
1898 if (_Store.Contains(folderID))
1899 {
1900 InventoryFolder inv = (InventoryFolder)Store[folderID];
1901 inv.Name = name;
1902 inv.ParentUUID = parentID;
1903 inv.PreferredType = type;
1904 _Store.UpdateNodeFor(inv);
1905 }
1906 }
1907  
1908 UpdateInventoryFolderPacket invFolder = new UpdateInventoryFolderPacket();
1909 invFolder.AgentData.AgentID = Client.Self.AgentID;
1910 invFolder.AgentData.SessionID = Client.Self.SessionID;
1911 invFolder.FolderData = new UpdateInventoryFolderPacket.FolderDataBlock[1];
1912 invFolder.FolderData[0] = new UpdateInventoryFolderPacket.FolderDataBlock();
1913 invFolder.FolderData[0].FolderID = folderID;
1914 invFolder.FolderData[0].ParentID = parentID;
1915 invFolder.FolderData[0].Name = Utils.StringToBytes(name);
1916 invFolder.FolderData[0].Type = (sbyte)type;
1917  
1918 Client.Network.SendPacket(invFolder);
1919 }
1920  
1921 /// <summary>
1922 /// Move a folder
1923 /// </summary>
1924 /// <param name="folderID">The source folders <seealso cref="UUID"/></param>
1925 /// <param name="newParentID">The destination folders <seealso cref="UUID"/></param>
1926 public void MoveFolder(UUID folderID, UUID newParentID)
1927 {
1928 lock (Store)
1929 {
1930 if (_Store.Contains(folderID))
1931 {
1932 InventoryBase inv = Store[folderID];
1933 inv.ParentUUID = newParentID;
1934 _Store.UpdateNodeFor(inv);
1935 }
1936 }
1937  
1938 MoveInventoryFolderPacket move = new MoveInventoryFolderPacket();
1939 move.AgentData.AgentID = Client.Self.AgentID;
1940 move.AgentData.SessionID = Client.Self.SessionID;
1941 move.AgentData.Stamp = false; //FIXME: ??
1942  
1943 move.InventoryData = new MoveInventoryFolderPacket.InventoryDataBlock[1];
1944 move.InventoryData[0] = new MoveInventoryFolderPacket.InventoryDataBlock();
1945 move.InventoryData[0].FolderID = folderID;
1946 move.InventoryData[0].ParentID = newParentID;
1947  
1948 Client.Network.SendPacket(move);
1949 }
1950  
1951 /// <summary>
1952 /// Move multiple folders, the keys in the Dictionary parameter,
1953 /// to a new parents, the value of that folder's key.
1954 /// </summary>
1955 /// <param name="foldersNewParents">A Dictionary containing the
1956 /// <seealso cref="UUID"/> of the source as the key, and the
1957 /// <seealso cref="UUID"/> of the destination as the value</param>
1958 public void MoveFolders(Dictionary<UUID, UUID> foldersNewParents)
1959 {
1960 // FIXME: Use two List<UUID> to stay consistent
1961  
1962 lock (Store)
1963 {
1964 foreach (KeyValuePair<UUID, UUID> entry in foldersNewParents)
1965 {
1966 if (_Store.Contains(entry.Key))
1967 {
1968 InventoryBase inv = _Store[entry.Key];
1969 inv.ParentUUID = entry.Value;
1970 _Store.UpdateNodeFor(inv);
1971 }
1972 }
1973 }
1974  
1975 //TODO: Test if this truly supports multiple-folder move
1976 MoveInventoryFolderPacket move = new MoveInventoryFolderPacket();
1977 move.AgentData.AgentID = Client.Self.AgentID;
1978 move.AgentData.SessionID = Client.Self.SessionID;
1979 move.AgentData.Stamp = false; //FIXME: ??
1980  
1981 move.InventoryData = new MoveInventoryFolderPacket.InventoryDataBlock[foldersNewParents.Count];
1982  
1983 int index = 0;
1984 foreach (KeyValuePair<UUID, UUID> folder in foldersNewParents)
1985 {
1986 MoveInventoryFolderPacket.InventoryDataBlock block = new MoveInventoryFolderPacket.InventoryDataBlock();
1987 block.FolderID = folder.Key;
1988 block.ParentID = folder.Value;
1989 move.InventoryData[index++] = block;
1990 }
1991  
1992 Client.Network.SendPacket(move);
1993 }
1994  
1995  
1996 /// <summary>
1997 /// Move an inventory item to a new folder
1998 /// </summary>
1999 /// <param name="itemID">The <seealso cref="UUID"/> of the source item to move</param>
2000 /// <param name="folderID">The <seealso cref="UUID"/> of the destination folder</param>
2001 public void MoveItem(UUID itemID, UUID folderID)
2002 {
2003 MoveItem(itemID, folderID, String.Empty);
2004 }
2005  
2006 /// <summary>
2007 /// Move and rename an inventory item
2008 /// </summary>
2009 /// <param name="itemID">The <seealso cref="UUID"/> of the source item to move</param>
2010 /// <param name="folderID">The <seealso cref="UUID"/> of the destination folder</param>
2011 /// <param name="newName">The name to change the folder to</param>
2012 public void MoveItem(UUID itemID, UUID folderID, string newName)
2013 {
2014 lock (_Store)
2015 {
2016 if (_Store.Contains(itemID))
2017 {
2018 InventoryBase inv = _Store[itemID];
2019 if (!string.IsNullOrEmpty(newName))
2020 {
2021 inv.Name = newName;
2022 }
2023 inv.ParentUUID = folderID;
2024 _Store.UpdateNodeFor(inv);
2025 }
2026 }
2027  
2028 MoveInventoryItemPacket move = new MoveInventoryItemPacket();
2029 move.AgentData.AgentID = Client.Self.AgentID;
2030 move.AgentData.SessionID = Client.Self.SessionID;
2031 move.AgentData.Stamp = false; //FIXME: ??
2032  
2033 move.InventoryData = new MoveInventoryItemPacket.InventoryDataBlock[1];
2034 move.InventoryData[0] = new MoveInventoryItemPacket.InventoryDataBlock();
2035 move.InventoryData[0].ItemID = itemID;
2036 move.InventoryData[0].FolderID = folderID;
2037 move.InventoryData[0].NewName = Utils.StringToBytes(newName);
2038  
2039 Client.Network.SendPacket(move);
2040 }
2041  
2042 /// <summary>
2043 /// Move multiple inventory items to new locations
2044 /// </summary>
2045 /// <param name="itemsNewParents">A Dictionary containing the
2046 /// <seealso cref="UUID"/> of the source item as the key, and the
2047 /// <seealso cref="UUID"/> of the destination folder as the value</param>
2048 public void MoveItems(Dictionary<UUID, UUID> itemsNewParents)
2049 {
2050 lock (_Store)
2051 {
2052 foreach (KeyValuePair<UUID, UUID> entry in itemsNewParents)
2053 {
2054 if (_Store.Contains(entry.Key))
2055 {
2056 InventoryBase inv = _Store[entry.Key];
2057 inv.ParentUUID = entry.Value;
2058 _Store.UpdateNodeFor(inv);
2059 }
2060 }
2061 }
2062  
2063 MoveInventoryItemPacket move = new MoveInventoryItemPacket();
2064 move.AgentData.AgentID = Client.Self.AgentID;
2065 move.AgentData.SessionID = Client.Self.SessionID;
2066 move.AgentData.Stamp = false; //FIXME: ??
2067  
2068 move.InventoryData = new MoveInventoryItemPacket.InventoryDataBlock[itemsNewParents.Count];
2069  
2070 int index = 0;
2071 foreach (KeyValuePair<UUID, UUID> entry in itemsNewParents)
2072 {
2073 MoveInventoryItemPacket.InventoryDataBlock block = new MoveInventoryItemPacket.InventoryDataBlock();
2074 block.ItemID = entry.Key;
2075 block.FolderID = entry.Value;
2076 block.NewName = Utils.EmptyBytes;
2077 move.InventoryData[index++] = block;
2078 }
2079  
2080 Client.Network.SendPacket(move);
2081 }
2082  
2083 #endregion Move
2084  
2085 #region Remove
2086  
2087 /// <summary>
2088 /// Remove descendants of a folder
2089 /// </summary>
2090 /// <param name="folder">The <seealso cref="UUID"/> of the folder</param>
2091 public void RemoveDescendants(UUID folder)
2092 {
2093 PurgeInventoryDescendentsPacket purge = new PurgeInventoryDescendentsPacket();
2094 purge.AgentData.AgentID = Client.Self.AgentID;
2095 purge.AgentData.SessionID = Client.Self.SessionID;
2096 purge.InventoryData.FolderID = folder;
2097 Client.Network.SendPacket(purge);
2098  
2099 // Update our local copy
2100 lock (_Store)
2101 {
2102 if (_Store.Contains(folder))
2103 {
2104 List<InventoryBase> contents = _Store.GetContents(folder);
2105 foreach (InventoryBase obj in contents)
2106 {
2107 _Store.RemoveNodeFor(obj);
2108 }
2109 }
2110 }
2111 }
2112  
2113 /// <summary>
2114 /// Remove a single item from inventory
2115 /// </summary>
2116 /// <param name="item">The <seealso cref="UUID"/> of the inventory item to remove</param>
2117 public void RemoveItem(UUID item)
2118 {
2119 List<UUID> items = new List<UUID>(1);
2120 items.Add(item);
2121  
2122 Remove(items, null);
2123 }
2124  
2125 /// <summary>
2126 /// Remove a folder from inventory
2127 /// </summary>
2128 /// <param name="folder">The <seealso cref="UUID"/> of the folder to remove</param>
2129 public void RemoveFolder(UUID folder)
2130 {
2131 List<UUID> folders = new List<UUID>(1);
2132 folders.Add(folder);
2133  
2134 Remove(null, folders);
2135 }
2136  
2137 /// <summary>
2138 /// Remove multiple items or folders from inventory
2139 /// </summary>
2140 /// <param name="items">A List containing the <seealso cref="UUID"/>s of items to remove</param>
2141 /// <param name="folders">A List containing the <seealso cref="UUID"/>s of the folders to remove</param>
2142 public void Remove(List<UUID> items, List<UUID> folders)
2143 {
2144 if ((items == null || items.Count == 0) && (folders == null || folders.Count == 0))
2145 return;
2146  
2147 RemoveInventoryObjectsPacket rem = new RemoveInventoryObjectsPacket();
2148 rem.AgentData.AgentID = Client.Self.AgentID;
2149 rem.AgentData.SessionID = Client.Self.SessionID;
2150  
2151 if (items == null || items.Count == 0)
2152 {
2153 // To indicate that we want no items removed:
2154 rem.ItemData = new RemoveInventoryObjectsPacket.ItemDataBlock[1];
2155 rem.ItemData[0] = new RemoveInventoryObjectsPacket.ItemDataBlock();
2156 rem.ItemData[0].ItemID = UUID.Zero;
2157 }
2158 else
2159 {
2160 lock (_Store)
2161 {
2162 rem.ItemData = new RemoveInventoryObjectsPacket.ItemDataBlock[items.Count];
2163 for (int i = 0; i < items.Count; i++)
2164 {
2165 rem.ItemData[i] = new RemoveInventoryObjectsPacket.ItemDataBlock();
2166 rem.ItemData[i].ItemID = items[i];
2167  
2168 // Update local copy
2169 if (_Store.Contains(items[i]))
2170 _Store.RemoveNodeFor(Store[items[i]]);
2171 }
2172 }
2173 }
2174  
2175 if (folders == null || folders.Count == 0)
2176 {
2177 // To indicate we want no folders removed:
2178 rem.FolderData = new RemoveInventoryObjectsPacket.FolderDataBlock[1];
2179 rem.FolderData[0] = new RemoveInventoryObjectsPacket.FolderDataBlock();
2180 rem.FolderData[0].FolderID = UUID.Zero;
2181 }
2182 else
2183 {
2184 lock (_Store)
2185 {
2186 rem.FolderData = new RemoveInventoryObjectsPacket.FolderDataBlock[folders.Count];
2187 for (int i = 0; i < folders.Count; i++)
2188 {
2189 rem.FolderData[i] = new RemoveInventoryObjectsPacket.FolderDataBlock();
2190 rem.FolderData[i].FolderID = folders[i];
2191  
2192 // Update local copy
2193 if (_Store.Contains(folders[i]))
2194 _Store.RemoveNodeFor(Store[folders[i]]);
2195 }
2196 }
2197 }
2198 Client.Network.SendPacket(rem);
2199 }
2200  
2201 /// <summary>
2202 /// Empty the Lost and Found folder
2203 /// </summary>
2204 public void EmptyLostAndFound()
2205 {
2206 EmptySystemFolder(AssetType.LostAndFoundFolder);
2207 }
2208  
2209 /// <summary>
2210 /// Empty the Trash folder
2211 /// </summary>
2212 public void EmptyTrash()
2213 {
2214 EmptySystemFolder(AssetType.TrashFolder);
2215 }
2216  
2217 private void EmptySystemFolder(AssetType folderType)
2218 {
2219 List<InventoryBase> items = _Store.GetContents(_Store.RootFolder);
2220  
2221 UUID folderKey = UUID.Zero;
2222 foreach (InventoryBase item in items)
2223 {
2224 if ((item as InventoryFolder) != null)
2225 {
2226 InventoryFolder folder = item as InventoryFolder;
2227 if (folder.PreferredType == folderType)
2228 {
2229 folderKey = folder.UUID;
2230 break;
2231 }
2232 }
2233 }
2234 items = _Store.GetContents(folderKey);
2235 List<UUID> remItems = new List<UUID>();
2236 List<UUID> remFolders = new List<UUID>();
2237 foreach (InventoryBase item in items)
2238 {
2239 if ((item as InventoryFolder) != null)
2240 {
2241 remFolders.Add(item.UUID);
2242 }
2243 else
2244 {
2245 remItems.Add(item.UUID);
2246 }
2247 }
2248 Remove(remItems, remFolders);
2249 }
2250 #endregion Remove
2251  
2252 #region Create
2253  
2254 /// <summary>
2255 ///
2256 /// </summary>
2257 /// <param name="parentFolder"></param>
2258 /// <param name="name"></param>
2259 /// <param name="description"></param>
2260 /// <param name="type"></param>
2261 /// <param name="assetTransactionID">Proper use is to upload the inventory's asset first, then provide the Asset's TransactionID here.</param>
2262 /// <param name="invType"></param>
2263 /// <param name="nextOwnerMask"></param>
2264 /// <param name="callback"></param>
2265 public void RequestCreateItem(UUID parentFolder, string name, string description, AssetType type, UUID assetTransactionID,
2266 InventoryType invType, PermissionMask nextOwnerMask, ItemCreatedCallback callback)
2267 {
2268 // Even though WearableType 0 is Shape, in this context it is treated as NOT_WEARABLE
2269 RequestCreateItem(parentFolder, name, description, type, assetTransactionID, invType, (WearableType)0, nextOwnerMask,
2270 callback);
2271 }
2272  
2273 /// <summary>
2274 ///
2275 /// </summary>
2276 /// <param name="parentFolder"></param>
2277 /// <param name="name"></param>
2278 /// <param name="description"></param>
2279 /// <param name="type"></param>
2280 /// <param name="assetTransactionID">Proper use is to upload the inventory's asset first, then provide the Asset's TransactionID here.</param>
2281 /// <param name="invType"></param>
2282 /// <param name="wearableType"></param>
2283 /// <param name="nextOwnerMask"></param>
2284 /// <param name="callback"></param>
2285 public void RequestCreateItem(UUID parentFolder, string name, string description, AssetType type, UUID assetTransactionID,
2286 InventoryType invType, WearableType wearableType, PermissionMask nextOwnerMask, ItemCreatedCallback callback)
2287 {
2288 CreateInventoryItemPacket create = new CreateInventoryItemPacket();
2289 create.AgentData.AgentID = Client.Self.AgentID;
2290 create.AgentData.SessionID = Client.Self.SessionID;
2291  
2292 create.InventoryBlock.CallbackID = RegisterItemCreatedCallback(callback);
2293 create.InventoryBlock.FolderID = parentFolder;
2294 create.InventoryBlock.TransactionID = assetTransactionID;
2295 create.InventoryBlock.NextOwnerMask = (uint)nextOwnerMask;
2296 create.InventoryBlock.Type = (sbyte)type;
2297 create.InventoryBlock.InvType = (sbyte)invType;
2298 create.InventoryBlock.WearableType = (byte)wearableType;
2299 create.InventoryBlock.Name = Utils.StringToBytes(name);
2300 create.InventoryBlock.Description = Utils.StringToBytes(description);
2301  
2302 Client.Network.SendPacket(create);
2303 }
2304  
2305 /// <summary>
2306 /// Creates a new inventory folder
2307 /// </summary>
2308 /// <param name="parentID">ID of the folder to put this folder in</param>
2309 /// <param name="name">Name of the folder to create</param>
2310 /// <returns>The UUID of the newly created folder</returns>
2311 public UUID CreateFolder(UUID parentID, string name)
2312 {
2313 return CreateFolder(parentID, name, AssetType.Unknown);
2314 }
2315  
2316 /// <summary>
2317 /// Creates a new inventory folder
2318 /// </summary>
2319 /// <param name="parentID">ID of the folder to put this folder in</param>
2320 /// <param name="name">Name of the folder to create</param>
2321 /// <param name="preferredType">Sets this folder as the default folder
2322 /// for new assets of the specified type. Use <code>AssetType.Unknown</code>
2323 /// to create a normal folder, otherwise it will likely create a
2324 /// duplicate of an existing folder type</param>
2325 /// <returns>The UUID of the newly created folder</returns>
2326 /// <remarks>If you specify a preferred type of <code>AsseType.Folder</code>
2327 /// it will create a new root folder which may likely cause all sorts
2328 /// of strange problems</remarks>
2329 public UUID CreateFolder(UUID parentID, string name, AssetType preferredType)
2330 {
2331 UUID id = UUID.Random();
2332  
2333 // Assign a folder name if one is not already set
2334 if (String.IsNullOrEmpty(name))
2335 {
2336 if (preferredType >= AssetType.Texture && preferredType <= AssetType.Gesture)
2337 {
2338 name = _NewFolderNames[(int)preferredType];
2339 }
2340 else
2341 {
2342 name = "New Folder";
2343 }
2344 }
2345  
2346 // Create the new folder locally
2347 InventoryFolder newFolder = new InventoryFolder(id);
2348 newFolder.Version = 1;
2349 newFolder.DescendentCount = 0;
2350 newFolder.ParentUUID = parentID;
2351 newFolder.PreferredType = preferredType;
2352 newFolder.Name = name;
2353 newFolder.OwnerID = Client.Self.AgentID;
2354  
2355 // Update the local store
2356 try { _Store[newFolder.UUID] = newFolder; }
2357 catch (InventoryException ie) { Logger.Log(ie.Message, Helpers.LogLevel.Warning, Client, ie); }
2358  
2359 // Create the create folder packet and send it
2360 CreateInventoryFolderPacket create = new CreateInventoryFolderPacket();
2361 create.AgentData.AgentID = Client.Self.AgentID;
2362 create.AgentData.SessionID = Client.Self.SessionID;
2363  
2364 create.FolderData.FolderID = id;
2365 create.FolderData.ParentID = parentID;
2366 create.FolderData.Type = (sbyte)preferredType;
2367 create.FolderData.Name = Utils.StringToBytes(name);
2368  
2369 Client.Network.SendPacket(create);
2370  
2371 return id;
2372 }
2373  
2374 /// <summary>
2375 /// Create an inventory item and upload asset data
2376 /// </summary>
2377 /// <param name="data">Asset data</param>
2378 /// <param name="name">Inventory item name</param>
2379 /// <param name="description">Inventory item description</param>
2380 /// <param name="assetType">Asset type</param>
2381 /// <param name="invType">Inventory type</param>
2382 /// <param name="folderID">Put newly created inventory in this folder</param>
2383 /// <param name="callback">Delegate that will receive feedback on success or failure</param>
2384 public void RequestCreateItemFromAsset(byte[] data, string name, string description, AssetType assetType,
2385 InventoryType invType, UUID folderID, ItemCreatedFromAssetCallback callback)
2386 {
2387 Permissions permissions = new Permissions();
2388 permissions.EveryoneMask = PermissionMask.None;
2389 permissions.GroupMask = PermissionMask.None;
2390 permissions.NextOwnerMask = PermissionMask.All;
2391  
2392 RequestCreateItemFromAsset(data, name, description, assetType, invType, folderID, permissions, callback);
2393 }
2394  
2395 /// <summary>
2396 /// Create an inventory item and upload asset data
2397 /// </summary>
2398 /// <param name="data">Asset data</param>
2399 /// <param name="name">Inventory item name</param>
2400 /// <param name="description">Inventory item description</param>
2401 /// <param name="assetType">Asset type</param>
2402 /// <param name="invType">Inventory type</param>
2403 /// <param name="folderID">Put newly created inventory in this folder</param>
2404 /// <param name="permissions">Permission of the newly created item
2405 /// (EveryoneMask, GroupMask, and NextOwnerMask of Permissions struct are supported)</param>
2406 /// <param name="callback">Delegate that will receive feedback on success or failure</param>
2407 public void RequestCreateItemFromAsset(byte[] data, string name, string description, AssetType assetType,
2408 InventoryType invType, UUID folderID, Permissions permissions, ItemCreatedFromAssetCallback callback)
2409 {
2410 if (Client.Network.CurrentSim == null || Client.Network.CurrentSim.Caps == null)
2411 throw new Exception("NewFileAgentInventory capability is not currently available");
2412  
2413 Uri url = Client.Network.CurrentSim.Caps.CapabilityURI("NewFileAgentInventory");
2414  
2415 if (url != null)
2416 {
2417 OSDMap query = new OSDMap();
2418 query.Add("folder_id", OSD.FromUUID(folderID));
2419 query.Add("asset_type", OSD.FromString(Utils.AssetTypeToString(assetType)));
2420 query.Add("inventory_type", OSD.FromString(Utils.InventoryTypeToString(invType)));
2421 query.Add("name", OSD.FromString(name));
2422 query.Add("description", OSD.FromString(description));
2423 query.Add("everyone_mask", OSD.FromInteger((int)permissions.EveryoneMask));
2424 query.Add("group_mask", OSD.FromInteger((int)permissions.GroupMask));
2425 query.Add("next_owner_mask", OSD.FromInteger((int)permissions.NextOwnerMask));
2426 query.Add("expected_upload_cost", OSD.FromInteger(Client.Settings.UPLOAD_COST));
2427  
2428 // Make the request
2429 CapsClient request = new CapsClient(url);
2430 request.OnComplete += CreateItemFromAssetResponse;
2431 request.UserData = new object[] { callback, data, Client.Settings.CAPS_TIMEOUT, query };
2432  
2433 request.BeginGetResponse(query, OSDFormat.Xml, Client.Settings.CAPS_TIMEOUT);
2434 }
2435 else
2436 {
2437 throw new Exception("NewFileAgentInventory capability is not currently available");
2438 }
2439 }
2440  
2441 /// <summary>
2442 /// Creates inventory link to another inventory item or folder
2443 /// </summary>
2444 /// <param name="folderID">Put newly created link in folder with this UUID</param>
2445 /// <param name="bse">Inventory item or folder</param>
2446 /// <param name="callback">Method to call upon creation of the link</param>
2447 public void CreateLink(UUID folderID, InventoryBase bse, ItemCreatedCallback callback)
2448 {
2449 if (bse is InventoryFolder)
2450 {
2451 InventoryFolder folder = (InventoryFolder)bse;
2452 CreateLink(folderID, folder, callback);
2453 }
2454 else if (bse is InventoryItem)
2455 {
2456 InventoryItem item = (InventoryItem)bse;
2457 CreateLink(folderID, item.UUID, item.Name, item.Description, AssetType.Link, item.InventoryType, UUID.Random(), callback);
2458 }
2459 }
2460  
2461 /// <summary>
2462 /// Creates inventory link to another inventory item
2463 /// </summary>
2464 /// <param name="folderID">Put newly created link in folder with this UUID</param>
2465 /// <param name="item">Original inventory item</param>
2466 /// <param name="callback">Method to call upon creation of the link</param>
2467 public void CreateLink(UUID folderID, InventoryItem item, ItemCreatedCallback callback)
2468 {
2469 CreateLink(folderID, item.UUID, item.Name, item.Description, AssetType.Link, item.InventoryType, UUID.Random(), callback);
2470 }
2471  
2472 /// <summary>
2473 /// Creates inventory link to another inventory folder
2474 /// </summary>
2475 /// <param name="folderID">Put newly created link in folder with this UUID</param>
2476 /// <param name="folder">Original inventory folder</param>
2477 /// <param name="callback">Method to call upon creation of the link</param>
2478 public void CreateLink(UUID folderID, InventoryFolder folder, ItemCreatedCallback callback)
2479 {
2480 CreateLink(folderID, folder.UUID, folder.Name, "", AssetType.LinkFolder, InventoryType.Folder, UUID.Random(), callback);
2481 }
2482  
2483 /// <summary>
2484 /// Creates inventory link to another inventory item or folder
2485 /// </summary>
2486 /// <param name="folderID">Put newly created link in folder with this UUID</param>
2487 /// <param name="itemID">Original item's UUID</param>
2488 /// <param name="name">Name</param>
2489 /// <param name="description">Description</param>
2490 /// <param name="assetType">Asset Type</param>
2491 /// <param name="invType">Inventory Type</param>
2492 /// <param name="transactionID">Transaction UUID</param>
2493 /// <param name="callback">Method to call upon creation of the link</param>
2494 public void CreateLink(UUID folderID, UUID itemID, string name, string description, AssetType assetType, InventoryType invType, UUID transactionID, ItemCreatedCallback callback)
2495 {
2496 LinkInventoryItemPacket create = new LinkInventoryItemPacket();
2497 create.AgentData.AgentID = Client.Self.AgentID;
2498 create.AgentData.SessionID = Client.Self.SessionID;
2499  
2500 create.InventoryBlock.CallbackID = RegisterItemCreatedCallback(callback);
2501 lock (_ItemInventoryTypeRequest)
2502 {
2503 _ItemInventoryTypeRequest[create.InventoryBlock.CallbackID] = invType;
2504 }
2505 create.InventoryBlock.FolderID = folderID;
2506 create.InventoryBlock.TransactionID = transactionID;
2507 create.InventoryBlock.OldItemID = itemID;
2508 create.InventoryBlock.Type = (sbyte)assetType;
2509 create.InventoryBlock.InvType = (sbyte)invType;
2510 create.InventoryBlock.Name = Utils.StringToBytes(name);
2511 create.InventoryBlock.Description = Utils.StringToBytes(description);
2512  
2513 Client.Network.SendPacket(create);
2514 }
2515  
2516 #endregion Create
2517  
2518 #region Copy
2519  
2520 /// <summary>
2521 ///
2522 /// </summary>
2523 /// <param name="item"></param>
2524 /// <param name="newParent"></param>
2525 /// <param name="newName"></param>
2526 /// <param name="callback"></param>
2527 public void RequestCopyItem(UUID item, UUID newParent, string newName, ItemCopiedCallback callback)
2528 {
2529 RequestCopyItem(item, newParent, newName, Client.Self.AgentID, callback);
2530 }
2531  
2532 /// <summary>
2533 ///
2534 /// </summary>
2535 /// <param name="item"></param>
2536 /// <param name="newParent"></param>
2537 /// <param name="newName"></param>
2538 /// <param name="oldOwnerID"></param>
2539 /// <param name="callback"></param>
2540 public void RequestCopyItem(UUID item, UUID newParent, string newName, UUID oldOwnerID,
2541 ItemCopiedCallback callback)
2542 {
2543 List<UUID> items = new List<UUID>(1);
2544 items.Add(item);
2545  
2546 List<UUID> folders = new List<UUID>(1);
2547 folders.Add(newParent);
2548  
2549 List<string> names = new List<string>(1);
2550 names.Add(newName);
2551  
2552 RequestCopyItems(items, folders, names, oldOwnerID, callback);
2553 }
2554  
2555 /// <summary>
2556 ///
2557 /// </summary>
2558 /// <param name="items"></param>
2559 /// <param name="targetFolders"></param>
2560 /// <param name="newNames"></param>
2561 /// <param name="oldOwnerID"></param>
2562 /// <param name="callback"></param>
2563 public void RequestCopyItems(List<UUID> items, List<UUID> targetFolders, List<string> newNames,
2564 UUID oldOwnerID, ItemCopiedCallback callback)
2565 {
2566 if (items.Count != targetFolders.Count || (newNames != null && items.Count != newNames.Count))
2567 throw new ArgumentException("All list arguments must have an equal number of entries");
2568  
2569 uint callbackID = RegisterItemsCopiedCallback(callback);
2570  
2571 CopyInventoryItemPacket copy = new CopyInventoryItemPacket();
2572 copy.AgentData.AgentID = Client.Self.AgentID;
2573 copy.AgentData.SessionID = Client.Self.SessionID;
2574  
2575 copy.InventoryData = new CopyInventoryItemPacket.InventoryDataBlock[items.Count];
2576 for (int i = 0; i < items.Count; ++i)
2577 {
2578 copy.InventoryData[i] = new CopyInventoryItemPacket.InventoryDataBlock();
2579 copy.InventoryData[i].CallbackID = callbackID;
2580 copy.InventoryData[i].NewFolderID = targetFolders[i];
2581 copy.InventoryData[i].OldAgentID = oldOwnerID;
2582 copy.InventoryData[i].OldItemID = items[i];
2583  
2584 if (newNames != null && !String.IsNullOrEmpty(newNames[i]))
2585 copy.InventoryData[i].NewName = Utils.StringToBytes(newNames[i]);
2586 else
2587 copy.InventoryData[i].NewName = Utils.EmptyBytes;
2588 }
2589  
2590 Client.Network.SendPacket(copy);
2591 }
2592  
2593 /// <summary>
2594 /// Request a copy of an asset embedded within a notecard
2595 /// </summary>
2596 /// <param name="objectID">Usually UUID.Zero for copying an asset from a notecard</param>
2597 /// <param name="notecardID">UUID of the notecard to request an asset from</param>
2598 /// <param name="folderID">Target folder for asset to go to in your inventory</param>
2599 /// <param name="itemID">UUID of the embedded asset</param>
2600 /// <param name="callback">callback to run when item is copied to inventory</param>
2601 public void RequestCopyItemFromNotecard(UUID objectID, UUID notecardID, UUID folderID, UUID itemID, ItemCopiedCallback callback)
2602 {
2603 _ItemCopiedCallbacks[0] = callback; //Notecards always use callback ID 0
2604  
2605 Uri url = Client.Network.CurrentSim.Caps.CapabilityURI("CopyInventoryFromNotecard");
2606  
2607 if (url != null)
2608 {
2609 CopyInventoryFromNotecardMessage message = new CopyInventoryFromNotecardMessage();
2610 message.CallbackID = 0;
2611 message.FolderID = folderID;
2612 message.ItemID = itemID;
2613 message.NotecardID = notecardID;
2614 message.ObjectID = objectID;
2615  
2616 CapsClient request = new CapsClient(url);
2617 request.BeginGetResponse(message.Serialize(), OSDFormat.Xml, Client.Settings.CAPS_TIMEOUT);
2618 }
2619 else
2620 {
2621 CopyInventoryFromNotecardPacket copy = new CopyInventoryFromNotecardPacket();
2622 copy.AgentData.AgentID = Client.Self.AgentID;
2623 copy.AgentData.SessionID = Client.Self.SessionID;
2624  
2625 copy.NotecardData.ObjectID = objectID;
2626 copy.NotecardData.NotecardItemID = notecardID;
2627  
2628 copy.InventoryData = new CopyInventoryFromNotecardPacket.InventoryDataBlock[1];
2629 copy.InventoryData[0] = new CopyInventoryFromNotecardPacket.InventoryDataBlock();
2630 copy.InventoryData[0].FolderID = folderID;
2631 copy.InventoryData[0].ItemID = itemID;
2632  
2633 Client.Network.SendPacket(copy);
2634 }
2635 }
2636  
2637 #endregion Copy
2638  
2639 #region Update
2640  
2641 /// <summary>
2642 ///
2643 /// </summary>
2644 /// <param name="item"></param>
2645 public void RequestUpdateItem(InventoryItem item)
2646 {
2647 List<InventoryItem> items = new List<InventoryItem>(1);
2648 items.Add(item);
2649  
2650 RequestUpdateItems(items, UUID.Random());
2651 }
2652  
2653 /// <summary>
2654 ///
2655 /// </summary>
2656 /// <param name="items"></param>
2657 public void RequestUpdateItems(List<InventoryItem> items)
2658 {
2659 RequestUpdateItems(items, UUID.Random());
2660 }
2661  
2662 /// <summary>
2663 ///
2664 /// </summary>
2665 /// <param name="items"></param>
2666 /// <param name="transactionID"></param>
2667 public void RequestUpdateItems(List<InventoryItem> items, UUID transactionID)
2668 {
2669 UpdateInventoryItemPacket update = new UpdateInventoryItemPacket();
2670 update.AgentData.AgentID = Client.Self.AgentID;
2671 update.AgentData.SessionID = Client.Self.SessionID;
2672 update.AgentData.TransactionID = transactionID;
2673  
2674 update.InventoryData = new UpdateInventoryItemPacket.InventoryDataBlock[items.Count];
2675 for (int i = 0; i < items.Count; i++)
2676 {
2677 InventoryItem item = items[i];
2678  
2679 UpdateInventoryItemPacket.InventoryDataBlock block = new UpdateInventoryItemPacket.InventoryDataBlock();
2680 block.BaseMask = (uint)item.Permissions.BaseMask;
2681 block.CRC = ItemCRC(item);
2682 block.CreationDate = (int)Utils.DateTimeToUnixTime(item.CreationDate);
2683 block.CreatorID = item.CreatorID;
2684 block.Description = Utils.StringToBytes(item.Description);
2685 block.EveryoneMask = (uint)item.Permissions.EveryoneMask;
2686 block.Flags = (uint)item.Flags;
2687 block.FolderID = item.ParentUUID;
2688 block.GroupID = item.GroupID;
2689 block.GroupMask = (uint)item.Permissions.GroupMask;
2690 block.GroupOwned = item.GroupOwned;
2691 block.InvType = (sbyte)item.InventoryType;
2692 block.ItemID = item.UUID;
2693 block.Name = Utils.StringToBytes(item.Name);
2694 block.NextOwnerMask = (uint)item.Permissions.NextOwnerMask;
2695 block.OwnerID = item.OwnerID;
2696 block.OwnerMask = (uint)item.Permissions.OwnerMask;
2697 block.SalePrice = item.SalePrice;
2698 block.SaleType = (byte)item.SaleType;
2699 block.TransactionID = item.TransactionID;
2700 block.Type = (sbyte)item.AssetType;
2701  
2702 update.InventoryData[i] = block;
2703 }
2704  
2705 Client.Network.SendPacket(update);
2706 }
2707  
2708 /// <summary>
2709 ///
2710 /// </summary>
2711 /// <param name="data"></param>
2712 /// <param name="notecardID"></param>
2713 /// <param name="callback"></param>
2714 public void RequestUploadNotecardAsset(byte[] data, UUID notecardID, InventoryUploadedAssetCallback callback)
2715 {
2716 if (Client.Network.CurrentSim == null || Client.Network.CurrentSim.Caps == null)
2717 throw new Exception("UpdateNotecardAgentInventory capability is not currently available");
2718  
2719 Uri url = Client.Network.CurrentSim.Caps.CapabilityURI("UpdateNotecardAgentInventory");
2720  
2721 if (url != null)
2722 {
2723 OSDMap query = new OSDMap();
2724 query.Add("item_id", OSD.FromUUID(notecardID));
2725  
2726 // Make the request
2727 CapsClient request = new CapsClient(url);
2728 request.OnComplete += UploadInventoryAssetResponse;
2729 request.UserData = new object[] { new KeyValuePair<InventoryUploadedAssetCallback, byte[]>(callback, data), notecardID };
2730 request.BeginGetResponse(query, OSDFormat.Xml, Client.Settings.CAPS_TIMEOUT);
2731 }
2732 else
2733 {
2734 throw new Exception("UpdateNotecardAgentInventory capability is not currently available");
2735 }
2736 }
2737  
2738 /// <summary>
2739 /// Save changes to notecard embedded in object contents
2740 /// </summary>
2741 /// <param name="data">Encoded notecard asset data</param>
2742 /// <param name="notecardID">Notecard UUID</param>
2743 /// <param name="taskID">Object's UUID</param>
2744 /// <param name="callback">Called upon finish of the upload with status information</param>
2745 public void RequestUpdateNotecardTask(byte[] data, UUID notecardID, UUID taskID, InventoryUploadedAssetCallback callback)
2746 {
2747 if (Client.Network.CurrentSim == null || Client.Network.CurrentSim.Caps == null)
2748 throw new Exception("UpdateNotecardTaskInventory capability is not currently available");
2749  
2750 Uri url = Client.Network.CurrentSim.Caps.CapabilityURI("UpdateNotecardTaskInventory");
2751  
2752 if (url != null)
2753 {
2754 OSDMap query = new OSDMap();
2755 query.Add("item_id", OSD.FromUUID(notecardID));
2756 query.Add("task_id", OSD.FromUUID(taskID));
2757  
2758 // Make the request
2759 CapsClient request = new CapsClient(url);
2760 request.OnComplete += UploadInventoryAssetResponse;
2761 request.UserData = new object[] { new KeyValuePair<InventoryUploadedAssetCallback, byte[]>(callback, data), notecardID };
2762 request.BeginGetResponse(query, OSDFormat.Xml, Client.Settings.CAPS_TIMEOUT);
2763 }
2764 else
2765 {
2766 throw new Exception("UpdateNotecardTaskInventory capability is not currently available");
2767 }
2768 }
2769  
2770 /// <summary>
2771 /// Upload new gesture asset for an inventory gesture item
2772 /// </summary>
2773 /// <param name="data">Encoded gesture asset</param>
2774 /// <param name="gestureID">Gesture inventory UUID</param>
2775 /// <param name="callback">Callback whick will be called when upload is complete</param>
2776 public void RequestUploadGestureAsset(byte[] data, UUID gestureID, InventoryUploadedAssetCallback callback)
2777 {
2778 if (Client.Network.CurrentSim == null || Client.Network.CurrentSim.Caps == null)
2779 throw new Exception("UpdateGestureAgentInventory capability is not currently available");
2780  
2781 Uri url = Client.Network.CurrentSim.Caps.CapabilityURI("UpdateGestureAgentInventory");
2782  
2783 if (url != null)
2784 {
2785 OSDMap query = new OSDMap();
2786 query.Add("item_id", OSD.FromUUID(gestureID));
2787  
2788 // Make the request
2789 CapsClient request = new CapsClient(url);
2790 request.OnComplete += UploadInventoryAssetResponse;
2791 request.UserData = new object[] { new KeyValuePair<InventoryUploadedAssetCallback, byte[]>(callback, data), gestureID };
2792 request.BeginGetResponse(query, OSDFormat.Xml, Client.Settings.CAPS_TIMEOUT);
2793 }
2794 else
2795 {
2796 throw new Exception("UpdateGestureAgentInventory capability is not currently available");
2797 }
2798 }
2799  
2800 /// <summary>
2801 /// Update an existing script in an agents Inventory
2802 /// </summary>
2803 /// <param name="data">A byte[] array containing the encoded scripts contents</param>
2804 /// <param name="itemID">the itemID of the script</param>
2805 /// <param name="mono">if true, sets the script content to run on the mono interpreter</param>
2806 /// <param name="callback"></param>
2807 public void RequestUpdateScriptAgentInventory(byte[] data, UUID itemID, bool mono, ScriptUpdatedCallback callback)
2808 {
2809 Uri url = Client.Network.CurrentSim.Caps.CapabilityURI("UpdateScriptAgent");
2810  
2811 if (url != null)
2812 {
2813 UpdateScriptAgentRequestMessage msg = new UpdateScriptAgentRequestMessage();
2814 msg.ItemID = itemID;
2815 msg.Target = mono ? "mono" : "lsl2";
2816  
2817 CapsClient request = new CapsClient(url);
2818 request.OnComplete += new CapsClient.CompleteCallback(UpdateScriptAgentInventoryResponse);
2819 request.UserData = new object[2] { new KeyValuePair<ScriptUpdatedCallback, byte[]>(callback, data), itemID };
2820 request.BeginGetResponse(msg.Serialize(), OSDFormat.Xml, Client.Settings.CAPS_TIMEOUT);
2821 }
2822 else
2823 {
2824 throw new Exception("UpdateScriptAgent capability is not currently available");
2825 }
2826 }
2827  
2828 /// <summary>
2829 /// Update an existing script in an task Inventory
2830 /// </summary>
2831 /// <param name="data">A byte[] array containing the encoded scripts contents</param>
2832 /// <param name="itemID">the itemID of the script</param>
2833 /// <param name="taskID">UUID of the prim containting the script</param>
2834 /// <param name="mono">if true, sets the script content to run on the mono interpreter</param>
2835 /// <param name="running">if true, sets the script to running</param>
2836 /// <param name="callback"></param>
2837 public void RequestUpdateScriptTask(byte[] data, UUID itemID, UUID taskID, bool mono, bool running, ScriptUpdatedCallback callback)
2838 {
2839 Uri url = Client.Network.CurrentSim.Caps.CapabilityURI("UpdateScriptTask");
2840  
2841 if (url != null)
2842 {
2843 UpdateScriptTaskUpdateMessage msg = new UpdateScriptTaskUpdateMessage();
2844 msg.ItemID = itemID;
2845 msg.TaskID = taskID;
2846 msg.ScriptRunning = running;
2847 msg.Target = mono ? "mono" : "lsl2";
2848  
2849 CapsClient request = new CapsClient(url);
2850 request.OnComplete += new CapsClient.CompleteCallback(UpdateScriptAgentInventoryResponse);
2851 request.UserData = new object[2] { new KeyValuePair<ScriptUpdatedCallback, byte[]>(callback, data), itemID };
2852 request.BeginGetResponse(msg.Serialize(), OSDFormat.Xml, Client.Settings.CAPS_TIMEOUT);
2853 }
2854 else
2855 {
2856 throw new Exception("UpdateScriptTask capability is not currently available");
2857 }
2858 }
2859 #endregion Update
2860  
2861 #region Rez/Give
2862  
2863 /// <summary>
2864 /// Rez an object from inventory
2865 /// </summary>
2866 /// <param name="simulator">Simulator to place object in</param>
2867 /// <param name="rotation">Rotation of the object when rezzed</param>
2868 /// <param name="position">Vector of where to place object</param>
2869 /// <param name="item">InventoryItem object containing item details</param>
2870 public UUID RequestRezFromInventory(Simulator simulator, Quaternion rotation, Vector3 position,
2871 InventoryItem item)
2872 {
2873 return RequestRezFromInventory(simulator, rotation, position, item, Client.Self.ActiveGroup,
2874 UUID.Random(), true);
2875 }
2876  
2877 /// <summary>
2878 /// Rez an object from inventory
2879 /// </summary>
2880 /// <param name="simulator">Simulator to place object in</param>
2881 /// <param name="rotation">Rotation of the object when rezzed</param>
2882 /// <param name="position">Vector of where to place object</param>
2883 /// <param name="item">InventoryItem object containing item details</param>
2884 /// <param name="groupOwner">UUID of group to own the object</param>
2885 public UUID RequestRezFromInventory(Simulator simulator, Quaternion rotation, Vector3 position,
2886 InventoryItem item, UUID groupOwner)
2887 {
2888 return RequestRezFromInventory(simulator, rotation, position, item, groupOwner, UUID.Random(), true);
2889 }
2890  
2891 /// <summary>
2892 /// Rez an object from inventory
2893 /// </summary>
2894 /// <param name="simulator">Simulator to place object in</param>
2895 /// <param name="rotation">Rotation of the object when rezzed</param>
2896 /// <param name="position">Vector of where to place object</param>
2897 /// <param name="item">InventoryItem object containing item details</param>
2898 /// <param name="groupOwner">UUID of group to own the object</param>
2899 /// <param name="queryID">User defined queryID to correlate replies</param>
2900 /// <param name="rezSelected">If set to true, the CreateSelected flag
2901 /// will be set on the rezzed object</param>
2902 public UUID RequestRezFromInventory(Simulator simulator, Quaternion rotation, Vector3 position,
2903 InventoryItem item, UUID groupOwner, UUID queryID, bool rezSelected)
2904 {
2905 return RequestRezFromInventory(simulator, UUID.Zero, rotation, position, item, groupOwner, queryID,
2906 rezSelected);
2907 }
2908  
2909 /// <summary>
2910 /// Rez an object from inventory
2911 /// </summary>
2912 /// <param name="simulator">Simulator to place object in</param>
2913 /// <param name="taskID">TaskID object when rezzed</param>
2914 /// <param name="rotation">Rotation of the object when rezzed</param>
2915 /// <param name="position">Vector of where to place object</param>
2916 /// <param name="item">InventoryItem object containing item details</param>
2917 /// <param name="groupOwner">UUID of group to own the object</param>
2918 /// <param name="queryID">User defined queryID to correlate replies</param>
2919 /// <param name="rezSelected">If set to true, the CreateSelected flag
2920 /// will be set on the rezzed object</param>
2921 public UUID RequestRezFromInventory(Simulator simulator, UUID taskID, Quaternion rotation, Vector3 position,
2922 InventoryItem item, UUID groupOwner, UUID queryID, bool rezSelected)
2923 {
2924 RezObjectPacket add = new RezObjectPacket();
2925  
2926 add.AgentData.AgentID = Client.Self.AgentID;
2927 add.AgentData.SessionID = Client.Self.SessionID;
2928 add.AgentData.GroupID = groupOwner;
2929  
2930 add.RezData.FromTaskID = taskID;
2931 add.RezData.BypassRaycast = 1;
2932 add.RezData.RayStart = position;
2933 add.RezData.RayEnd = position;
2934 add.RezData.RayTargetID = UUID.Zero;
2935 add.RezData.RayEndIsIntersection = false;
2936 add.RezData.RezSelected = rezSelected;
2937 add.RezData.RemoveItem = false;
2938 add.RezData.ItemFlags = (uint)item.Flags;
2939 add.RezData.GroupMask = (uint)item.Permissions.GroupMask;
2940 add.RezData.EveryoneMask = (uint)item.Permissions.EveryoneMask;
2941 add.RezData.NextOwnerMask = (uint)item.Permissions.NextOwnerMask;
2942  
2943 add.InventoryData.ItemID = item.UUID;
2944 add.InventoryData.FolderID = item.ParentUUID;
2945 add.InventoryData.CreatorID = item.CreatorID;
2946 add.InventoryData.OwnerID = item.OwnerID;
2947 add.InventoryData.GroupID = item.GroupID;
2948 add.InventoryData.BaseMask = (uint)item.Permissions.BaseMask;
2949 add.InventoryData.OwnerMask = (uint)item.Permissions.OwnerMask;
2950 add.InventoryData.GroupMask = (uint)item.Permissions.GroupMask;
2951 add.InventoryData.EveryoneMask = (uint)item.Permissions.EveryoneMask;
2952 add.InventoryData.NextOwnerMask = (uint)item.Permissions.NextOwnerMask;
2953 add.InventoryData.GroupOwned = item.GroupOwned;
2954 add.InventoryData.TransactionID = queryID;
2955 add.InventoryData.Type = (sbyte)item.InventoryType;
2956 add.InventoryData.InvType = (sbyte)item.InventoryType;
2957 add.InventoryData.Flags = (uint)item.Flags;
2958 add.InventoryData.SaleType = (byte)item.SaleType;
2959 add.InventoryData.SalePrice = item.SalePrice;
2960 add.InventoryData.Name = Utils.StringToBytes(item.Name);
2961 add.InventoryData.Description = Utils.StringToBytes(item.Description);
2962 add.InventoryData.CreationDate = (int)Utils.DateTimeToUnixTime(item.CreationDate);
2963  
2964 Client.Network.SendPacket(add, simulator);
2965  
2966 // Remove from store if the item is no copy
2967 if (Store.Items.ContainsKey(item.UUID) && Store[item.UUID] is InventoryItem)
2968 {
2969 InventoryItem invItem = (InventoryItem)Store[item.UUID];
2970 if ((invItem.Permissions.OwnerMask & PermissionMask.Copy) == PermissionMask.None)
2971 {
2972 Store.RemoveNodeFor(invItem);
2973 }
2974 }
2975  
2976 return queryID;
2977 }
2978  
2979 /// <summary>
2980 /// DeRez an object from the simulator to the agents Objects folder in the agents Inventory
2981 /// </summary>
2982 /// <param name="objectLocalID">The simulator Local ID of the object</param>
2983 /// <remarks>If objectLocalID is a child primitive in a linkset, the entire linkset will be derezzed</remarks>
2984 public void RequestDeRezToInventory(uint objectLocalID)
2985 {
2986 RequestDeRezToInventory(objectLocalID, DeRezDestination.AgentInventoryTake,
2987 Client.Inventory.FindFolderForType(AssetType.Object), UUID.Random());
2988 }
2989  
2990 /// <summary>
2991 /// DeRez an object from the simulator and return to inventory
2992 /// </summary>
2993 /// <param name="objectLocalID">The simulator Local ID of the object</param>
2994 /// <param name="destType">The type of destination from the <seealso cref="DeRezDestination"/> enum</param>
2995 /// <param name="destFolder">The destination inventory folders <seealso cref="UUID"/> -or-
2996 /// if DeRezzing object to a tasks Inventory, the Tasks <seealso cref="UUID"/></param>
2997 /// <param name="transactionID">The transaction ID for this request which
2998 /// can be used to correlate this request with other packets</param>
2999 /// <remarks>If objectLocalID is a child primitive in a linkset, the entire linkset will be derezzed</remarks>
3000 public void RequestDeRezToInventory(uint objectLocalID, DeRezDestination destType, UUID destFolder, UUID transactionID)
3001 {
3002 DeRezObjectPacket take = new DeRezObjectPacket();
3003  
3004 take.AgentData.AgentID = Client.Self.AgentID;
3005 take.AgentData.SessionID = Client.Self.SessionID;
3006 take.AgentBlock = new DeRezObjectPacket.AgentBlockBlock();
3007 take.AgentBlock.GroupID = UUID.Zero;
3008 take.AgentBlock.Destination = (byte)destType;
3009 take.AgentBlock.DestinationID = destFolder;
3010 take.AgentBlock.PacketCount = 1;
3011 take.AgentBlock.PacketNumber = 1;
3012 take.AgentBlock.TransactionID = transactionID;
3013  
3014 take.ObjectData = new DeRezObjectPacket.ObjectDataBlock[1];
3015 take.ObjectData[0] = new DeRezObjectPacket.ObjectDataBlock();
3016 take.ObjectData[0].ObjectLocalID = objectLocalID;
3017  
3018 Client.Network.SendPacket(take);
3019 }
3020  
3021 /// <summary>
3022 /// Rez an item from inventory to its previous simulator location
3023 /// </summary>
3024 /// <param name="simulator"></param>
3025 /// <param name="item"></param>
3026 /// <param name="queryID"></param>
3027 /// <returns></returns>
3028 public UUID RequestRestoreRezFromInventory(Simulator simulator, InventoryItem item, UUID queryID)
3029 {
3030 RezRestoreToWorldPacket add = new RezRestoreToWorldPacket();
3031  
3032 add.AgentData.AgentID = Client.Self.AgentID;
3033 add.AgentData.SessionID = Client.Self.SessionID;
3034  
3035 add.InventoryData.ItemID = item.UUID;
3036 add.InventoryData.FolderID = item.ParentUUID;
3037 add.InventoryData.CreatorID = item.CreatorID;
3038 add.InventoryData.OwnerID = item.OwnerID;
3039 add.InventoryData.GroupID = item.GroupID;
3040 add.InventoryData.BaseMask = (uint)item.Permissions.BaseMask;
3041 add.InventoryData.OwnerMask = (uint)item.Permissions.OwnerMask;
3042 add.InventoryData.GroupMask = (uint)item.Permissions.GroupMask;
3043 add.InventoryData.EveryoneMask = (uint)item.Permissions.EveryoneMask;
3044 add.InventoryData.NextOwnerMask = (uint)item.Permissions.NextOwnerMask;
3045 add.InventoryData.GroupOwned = item.GroupOwned;
3046 add.InventoryData.TransactionID = queryID;
3047 add.InventoryData.Type = (sbyte)item.InventoryType;
3048 add.InventoryData.InvType = (sbyte)item.InventoryType;
3049 add.InventoryData.Flags = (uint)item.Flags;
3050 add.InventoryData.SaleType = (byte)item.SaleType;
3051 add.InventoryData.SalePrice = item.SalePrice;
3052 add.InventoryData.Name = Utils.StringToBytes(item.Name);
3053 add.InventoryData.Description = Utils.StringToBytes(item.Description);
3054 add.InventoryData.CreationDate = (int)Utils.DateTimeToUnixTime(item.CreationDate);
3055  
3056 Client.Network.SendPacket(add, simulator);
3057  
3058 return queryID;
3059 }
3060  
3061 /// <summary>
3062 /// Give an inventory item to another avatar
3063 /// </summary>
3064 /// <param name="itemID">The <seealso cref="UUID"/> of the item to give</param>
3065 /// <param name="itemName">The name of the item</param>
3066 /// <param name="assetType">The type of the item from the <seealso cref="AssetType"/> enum</param>
3067 /// <param name="recipient">The <seealso cref="UUID"/> of the recipient</param>
3068 /// <param name="doEffect">true to generate a beameffect during transfer</param>
3069 public void GiveItem(UUID itemID, string itemName, AssetType assetType, UUID recipient,
3070 bool doEffect)
3071 {
3072 byte[] bucket;
3073  
3074  
3075 bucket = new byte[17];
3076 bucket[0] = (byte)assetType;
3077 Buffer.BlockCopy(itemID.GetBytes(), 0, bucket, 1, 16);
3078  
3079 Client.Self.InstantMessage(
3080 Client.Self.Name,
3081 recipient,
3082 itemName,
3083 UUID.Random(),
3084 InstantMessageDialog.InventoryOffered,
3085 InstantMessageOnline.Online,
3086 Client.Self.SimPosition,
3087 Client.Network.CurrentSim.ID,
3088 bucket);
3089  
3090 if (doEffect)
3091 {
3092 Client.Self.BeamEffect(Client.Self.AgentID, recipient, Vector3d.Zero,
3093 Client.Settings.DEFAULT_EFFECT_COLOR, 1f, UUID.Random());
3094 }
3095  
3096 // Remove from store if the item is no copy
3097 if (Store.Items.ContainsKey(itemID) && Store[itemID] is InventoryItem)
3098 {
3099 InventoryItem invItem = (InventoryItem)Store[itemID];
3100 if ((invItem.Permissions.OwnerMask & PermissionMask.Copy) == PermissionMask.None)
3101 {
3102 Store.RemoveNodeFor(invItem);
3103 }
3104 }
3105 }
3106  
3107 /// <summary>
3108 /// Give an inventory Folder with contents to another avatar
3109 /// </summary>
3110 /// <param name="folderID">The <seealso cref="UUID"/> of the Folder to give</param>
3111 /// <param name="folderName">The name of the folder</param>
3112 /// <param name="assetType">The type of the item from the <seealso cref="AssetType"/> enum</param>
3113 /// <param name="recipient">The <seealso cref="UUID"/> of the recipient</param>
3114 /// <param name="doEffect">true to generate a beameffect during transfer</param>
3115 public void GiveFolder(UUID folderID, string folderName, AssetType assetType, UUID recipient,
3116 bool doEffect)
3117 {
3118 byte[] bucket;
3119  
3120 List<InventoryItem> folderContents = new List<InventoryItem>();
3121  
3122 Client.Inventory.FolderContents(folderID, Client.Self.AgentID, false, true, InventorySortOrder.ByDate, 1000 * 15).ForEach(
3123 delegate(InventoryBase ib)
3124 {
3125 folderContents.Add(Client.Inventory.FetchItem(ib.UUID, Client.Self.AgentID, 1000 * 10));
3126 });
3127 bucket = new byte[17 * (folderContents.Count + 1)];
3128  
3129 //Add parent folder (first item in bucket)
3130 bucket[0] = (byte)assetType;
3131 Buffer.BlockCopy(folderID.GetBytes(), 0, bucket, 1, 16);
3132  
3133 //Add contents to bucket after folder
3134 for (int i = 1; i <= folderContents.Count; ++i)
3135 {
3136 bucket[i * 17] = (byte)folderContents[i - 1].AssetType;
3137 Buffer.BlockCopy(folderContents[i - 1].UUID.GetBytes(), 0, bucket, i * 17 + 1, 16);
3138 }
3139  
3140 Client.Self.InstantMessage(
3141 Client.Self.Name,
3142 recipient,
3143 folderName,
3144 UUID.Random(),
3145 InstantMessageDialog.InventoryOffered,
3146 InstantMessageOnline.Online,
3147 Client.Self.SimPosition,
3148 Client.Network.CurrentSim.ID,
3149 bucket);
3150  
3151 if (doEffect)
3152 {
3153 Client.Self.BeamEffect(Client.Self.AgentID, recipient, Vector3d.Zero,
3154 Client.Settings.DEFAULT_EFFECT_COLOR, 1f, UUID.Random());
3155 }
3156  
3157 // Remove from store if items were no copy
3158 for (int i = 0; i < folderContents.Count; i++)
3159 {
3160  
3161 if (Store.Items.ContainsKey(folderContents[i].UUID) && Store[folderContents[i].UUID] is InventoryItem)
3162 {
3163 InventoryItem invItem = (InventoryItem)Store[folderContents[i].UUID];
3164 if ((invItem.Permissions.OwnerMask & PermissionMask.Copy) == PermissionMask.None)
3165 {
3166 Store.RemoveNodeFor(invItem);
3167 }
3168 }
3169  
3170 }
3171 }
3172  
3173 #endregion Rez/Give
3174  
3175 #region Task
3176  
3177 /// <summary>
3178 /// Copy or move an <see cref="InventoryItem"/> from agent inventory to a task (primitive) inventory
3179 /// </summary>
3180 /// <param name="objectLocalID">The target object</param>
3181 /// <param name="item">The item to copy or move from inventory</param>
3182 /// <returns></returns>
3183 /// <remarks>For items with copy permissions a copy of the item is placed in the tasks inventory,
3184 /// for no-copy items the object is moved to the tasks inventory</remarks>
3185 // DocTODO: what does the return UUID correlate to if anything?
3186 public UUID UpdateTaskInventory(uint objectLocalID, InventoryItem item)
3187 {
3188 UUID transactionID = UUID.Random();
3189  
3190 UpdateTaskInventoryPacket update = new UpdateTaskInventoryPacket();
3191 update.AgentData.AgentID = Client.Self.AgentID;
3192 update.AgentData.SessionID = Client.Self.SessionID;
3193 update.UpdateData.Key = 0;
3194 update.UpdateData.LocalID = objectLocalID;
3195  
3196 update.InventoryData.ItemID = item.UUID;
3197 update.InventoryData.FolderID = item.ParentUUID;
3198 update.InventoryData.CreatorID = item.CreatorID;
3199 update.InventoryData.OwnerID = item.OwnerID;
3200 update.InventoryData.GroupID = item.GroupID;
3201 update.InventoryData.BaseMask = (uint)item.Permissions.BaseMask;
3202 update.InventoryData.OwnerMask = (uint)item.Permissions.OwnerMask;
3203 update.InventoryData.GroupMask = (uint)item.Permissions.GroupMask;
3204 update.InventoryData.EveryoneMask = (uint)item.Permissions.EveryoneMask;
3205 update.InventoryData.NextOwnerMask = (uint)item.Permissions.NextOwnerMask;
3206 update.InventoryData.GroupOwned = item.GroupOwned;
3207 update.InventoryData.TransactionID = transactionID;
3208 update.InventoryData.Type = (sbyte)item.AssetType;
3209 update.InventoryData.InvType = (sbyte)item.InventoryType;
3210 update.InventoryData.Flags = (uint)item.Flags;
3211 update.InventoryData.SaleType = (byte)item.SaleType;
3212 update.InventoryData.SalePrice = item.SalePrice;
3213 update.InventoryData.Name = Utils.StringToBytes(item.Name);
3214 update.InventoryData.Description = Utils.StringToBytes(item.Description);
3215 update.InventoryData.CreationDate = (int)Utils.DateTimeToUnixTime(item.CreationDate);
3216 update.InventoryData.CRC = ItemCRC(item);
3217  
3218 Client.Network.SendPacket(update);
3219  
3220 return transactionID;
3221 }
3222  
3223 /// <summary>
3224 /// Retrieve a listing of the items contained in a task (Primitive)
3225 /// </summary>
3226 /// <param name="objectID">The tasks <seealso cref="UUID"/></param>
3227 /// <param name="objectLocalID">The tasks simulator local ID</param>
3228 /// <param name="timeoutMS">milliseconds to wait for reply from simulator</param>
3229 /// <returns>A list containing the inventory items inside the task or null
3230 /// if a timeout occurs</returns>
3231 /// <remarks>This request blocks until the response from the simulator arrives
3232 /// or timeoutMS is exceeded</remarks>
3233 public List<InventoryBase> GetTaskInventory(UUID objectID, UInt32 objectLocalID, Int32 timeoutMS)
3234 {
3235 String filename = null;
3236 AutoResetEvent taskReplyEvent = new AutoResetEvent(false);
3237  
3238 EventHandler<TaskInventoryReplyEventArgs> callback =
3239 delegate(object sender, TaskInventoryReplyEventArgs e)
3240 {
3241 if (e.ItemID == objectID)
3242 {
3243 filename = e.AssetFilename;
3244 taskReplyEvent.Set();
3245 }
3246 };
3247  
3248 TaskInventoryReply += callback;
3249  
3250 RequestTaskInventory(objectLocalID);
3251  
3252 if (taskReplyEvent.WaitOne(timeoutMS, false))
3253 {
3254 TaskInventoryReply -= callback;
3255  
3256 if (!String.IsNullOrEmpty(filename))
3257 {
3258 byte[] assetData = null;
3259 ulong xferID = 0;
3260 AutoResetEvent taskDownloadEvent = new AutoResetEvent(false);
3261  
3262 EventHandler<XferReceivedEventArgs> xferCallback =
3263 delegate(object sender, XferReceivedEventArgs e)
3264 {
3265 if (e.Xfer.XferID == xferID)
3266 {
3267 assetData = e.Xfer.AssetData;
3268 taskDownloadEvent.Set();
3269 }
3270 };
3271  
3272 Client.Assets.XferReceived += xferCallback;
3273  
3274 // Start the actual asset xfer
3275 xferID = Client.Assets.RequestAssetXfer(filename, true, false, UUID.Zero, AssetType.Unknown, true);
3276  
3277 if (taskDownloadEvent.WaitOne(timeoutMS, false))
3278 {
3279 Client.Assets.XferReceived -= xferCallback;
3280  
3281 String taskList = Utils.BytesToString(assetData);
3282 return ParseTaskInventory(taskList);
3283 }
3284 else
3285 {
3286 Logger.Log("Timed out waiting for task inventory download for " + filename, Helpers.LogLevel.Warning, Client);
3287 Client.Assets.XferReceived -= xferCallback;
3288 return null;
3289 }
3290 }
3291 else
3292 {
3293 Logger.DebugLog("Task is empty for " + objectLocalID, Client);
3294 return new List<InventoryBase>(0);
3295 }
3296 }
3297 else
3298 {
3299 Logger.Log("Timed out waiting for task inventory reply for " + objectLocalID, Helpers.LogLevel.Warning, Client);
3300 TaskInventoryReply -= callback;
3301 return null;
3302 }
3303 }
3304  
3305 /// <summary>
3306 /// Request the contents of a tasks (primitives) inventory from the
3307 /// current simulator
3308 /// </summary>
3309 /// <param name="objectLocalID">The LocalID of the object</param>
3310 /// <seealso cref="TaskInventoryReply"/>
3311 public void RequestTaskInventory(uint objectLocalID)
3312 {
3313 RequestTaskInventory(objectLocalID, Client.Network.CurrentSim);
3314 }
3315  
3316 /// <summary>
3317 /// Request the contents of a tasks (primitives) inventory
3318 /// </summary>
3319 /// <param name="objectLocalID">The simulator Local ID of the object</param>
3320 /// <param name="simulator">A reference to the simulator object that contains the object</param>
3321 /// <seealso cref="TaskInventoryReply"/>
3322 public void RequestTaskInventory(uint objectLocalID, Simulator simulator)
3323 {
3324 RequestTaskInventoryPacket request = new RequestTaskInventoryPacket();
3325 request.AgentData.AgentID = Client.Self.AgentID;
3326 request.AgentData.SessionID = Client.Self.SessionID;
3327 request.InventoryData.LocalID = objectLocalID;
3328  
3329 Client.Network.SendPacket(request, simulator);
3330 }
3331  
3332 /// <summary>
3333 /// Move an item from a tasks (Primitive) inventory to the specified folder in the avatars inventory
3334 /// </summary>
3335 /// <param name="objectLocalID">LocalID of the object in the simulator</param>
3336 /// <param name="taskItemID">UUID of the task item to move</param>
3337 /// <param name="inventoryFolderID">The ID of the destination folder in this agents inventory</param>
3338 /// <param name="simulator">Simulator Object</param>
3339 /// <remarks>Raises the <see cref="OnTaskItemReceived"/> event</remarks>
3340 public void MoveTaskInventory(uint objectLocalID, UUID taskItemID, UUID inventoryFolderID, Simulator simulator)
3341 {
3342 MoveTaskInventoryPacket request = new MoveTaskInventoryPacket();
3343 request.AgentData.AgentID = Client.Self.AgentID;
3344 request.AgentData.SessionID = Client.Self.SessionID;
3345  
3346 request.AgentData.FolderID = inventoryFolderID;
3347  
3348 request.InventoryData.ItemID = taskItemID;
3349 request.InventoryData.LocalID = objectLocalID;
3350  
3351 Client.Network.SendPacket(request, simulator);
3352 }
3353  
3354 /// <summary>
3355 /// Remove an item from an objects (Prim) Inventory
3356 /// </summary>
3357 /// <param name="objectLocalID">LocalID of the object in the simulator</param>
3358 /// <param name="taskItemID">UUID of the task item to remove</param>
3359 /// <param name="simulator">Simulator Object</param>
3360 /// <remarks>You can confirm the removal by comparing the tasks inventory serial before and after the
3361 /// request with the <see cref="RequestTaskInventory"/> request combined with
3362 /// the <seealso cref="TaskInventoryReply"/> event</remarks>
3363 public void RemoveTaskInventory(uint objectLocalID, UUID taskItemID, Simulator simulator)
3364 {
3365 RemoveTaskInventoryPacket remove = new RemoveTaskInventoryPacket();
3366 remove.AgentData.AgentID = Client.Self.AgentID;
3367 remove.AgentData.SessionID = Client.Self.SessionID;
3368  
3369 remove.InventoryData.ItemID = taskItemID;
3370 remove.InventoryData.LocalID = objectLocalID;
3371  
3372 Client.Network.SendPacket(remove, simulator);
3373 }
3374  
3375 /// <summary>
3376 /// Copy an InventoryScript item from the Agents Inventory into a primitives task inventory
3377 /// </summary>
3378 /// <param name="objectLocalID">An unsigned integer representing a primitive being simulated</param>
3379 /// <param name="item">An <seealso cref="InventoryItem"/> which represents a script object from the agents inventory</param>
3380 /// <param name="enableScript">true to set the scripts running state to enabled</param>
3381 /// <returns>A Unique Transaction ID</returns>
3382 /// <example>
3383 /// The following example shows the basic steps necessary to copy a script from the agents inventory into a tasks inventory
3384 /// and assumes the script exists in the agents inventory.
3385 /// <code>
3386 /// uint primID = 95899503; // Fake prim ID
3387 /// UUID scriptID = UUID.Parse("92a7fe8a-e949-dd39-a8d8-1681d8673232"); // Fake Script UUID in Inventory
3388 ///
3389 /// Client.Inventory.FolderContents(Client.Inventory.FindFolderForType(AssetType.LSLText), Client.Self.AgentID,
3390 /// false, true, InventorySortOrder.ByName, 10000);
3391 ///
3392 /// Client.Inventory.RezScript(primID, (InventoryItem)Client.Inventory.Store[scriptID]);
3393 /// </code>
3394 /// </example>
3395 // DocTODO: what does the return UUID correlate to if anything?
3396 public UUID CopyScriptToTask(uint objectLocalID, InventoryItem item, bool enableScript)
3397 {
3398 UUID transactionID = UUID.Random();
3399  
3400 RezScriptPacket ScriptPacket = new RezScriptPacket();
3401 ScriptPacket.AgentData.AgentID = Client.Self.AgentID;
3402 ScriptPacket.AgentData.SessionID = Client.Self.SessionID;
3403  
3404 ScriptPacket.UpdateBlock.ObjectLocalID = objectLocalID;
3405 ScriptPacket.UpdateBlock.Enabled = enableScript;
3406  
3407 ScriptPacket.InventoryBlock.ItemID = item.UUID;
3408 ScriptPacket.InventoryBlock.FolderID = item.ParentUUID;
3409 ScriptPacket.InventoryBlock.CreatorID = item.CreatorID;
3410 ScriptPacket.InventoryBlock.OwnerID = item.OwnerID;
3411 ScriptPacket.InventoryBlock.GroupID = item.GroupID;
3412 ScriptPacket.InventoryBlock.BaseMask = (uint)item.Permissions.BaseMask;
3413 ScriptPacket.InventoryBlock.OwnerMask = (uint)item.Permissions.OwnerMask;
3414 ScriptPacket.InventoryBlock.GroupMask = (uint)item.Permissions.GroupMask;
3415 ScriptPacket.InventoryBlock.EveryoneMask = (uint)item.Permissions.EveryoneMask;
3416 ScriptPacket.InventoryBlock.NextOwnerMask = (uint)item.Permissions.NextOwnerMask;
3417 ScriptPacket.InventoryBlock.GroupOwned = item.GroupOwned;
3418 ScriptPacket.InventoryBlock.TransactionID = transactionID;
3419 ScriptPacket.InventoryBlock.Type = (sbyte)item.AssetType;
3420 ScriptPacket.InventoryBlock.InvType = (sbyte)item.InventoryType;
3421 ScriptPacket.InventoryBlock.Flags = (uint)item.Flags;
3422 ScriptPacket.InventoryBlock.SaleType = (byte)item.SaleType;
3423 ScriptPacket.InventoryBlock.SalePrice = item.SalePrice;
3424 ScriptPacket.InventoryBlock.Name = Utils.StringToBytes(item.Name);
3425 ScriptPacket.InventoryBlock.Description = Utils.StringToBytes(item.Description);
3426 ScriptPacket.InventoryBlock.CreationDate = (int)Utils.DateTimeToUnixTime(item.CreationDate);
3427 ScriptPacket.InventoryBlock.CRC = ItemCRC(item);
3428  
3429 Client.Network.SendPacket(ScriptPacket);
3430  
3431 return transactionID;
3432 }
3433  
3434  
3435 /// <summary>
3436 /// Request the running status of a script contained in a task (primitive) inventory
3437 /// </summary>
3438 /// <param name="objectID">The ID of the primitive containing the script</param>
3439 /// <param name="scriptID">The ID of the script</param>
3440 /// <remarks>The <see cref="ScriptRunningReply"/> event can be used to obtain the results of the
3441 /// request</remarks>
3442 /// <seealso cref="ScriptRunningReply"/>
3443 public void RequestGetScriptRunning(UUID objectID, UUID scriptID)
3444 {
3445 GetScriptRunningPacket request = new GetScriptRunningPacket();
3446 request.Script.ObjectID = objectID;
3447 request.Script.ItemID = scriptID;
3448  
3449 Client.Network.SendPacket(request);
3450 }
3451  
3452 /// <summary>
3453 /// Send a request to set the running state of a script contained in a task (primitive) inventory
3454 /// </summary>
3455 /// <param name="objectID">The ID of the primitive containing the script</param>
3456 /// <param name="scriptID">The ID of the script</param>
3457 /// <param name="running">true to set the script running, false to stop a running script</param>
3458 /// <remarks>To verify the change you can use the <see cref="RequestGetScriptRunning"/> method combined
3459 /// with the <see cref="ScriptRunningReply"/> event</remarks>
3460 public void RequestSetScriptRunning(UUID objectID, UUID scriptID, bool running)
3461 {
3462 SetScriptRunningPacket request = new SetScriptRunningPacket();
3463 request.AgentData.AgentID = Client.Self.AgentID;
3464 request.AgentData.SessionID = Client.Self.SessionID;
3465 request.Script.Running = running;
3466 request.Script.ItemID = scriptID;
3467 request.Script.ObjectID = objectID;
3468  
3469 Client.Network.SendPacket(request);
3470 }
3471  
3472 #endregion Task
3473  
3474 #region Helper Functions
3475  
3476 private uint RegisterItemCreatedCallback(ItemCreatedCallback callback)
3477 {
3478 lock (_CallbacksLock)
3479 {
3480 if (_CallbackPos == UInt32.MaxValue)
3481 _CallbackPos = 0;
3482  
3483 _CallbackPos++;
3484  
3485 if (_ItemCreatedCallbacks.ContainsKey(_CallbackPos))
3486 Logger.Log("Overwriting an existing ItemCreatedCallback", Helpers.LogLevel.Warning, Client);
3487  
3488 _ItemCreatedCallbacks[_CallbackPos] = callback;
3489  
3490 return _CallbackPos;
3491 }
3492 }
3493  
3494 private uint RegisterItemsCopiedCallback(ItemCopiedCallback callback)
3495 {
3496 lock (_CallbacksLock)
3497 {
3498 if (_CallbackPos == UInt32.MaxValue)
3499 _CallbackPos = 0;
3500  
3501 _CallbackPos++;
3502  
3503 if (_ItemCopiedCallbacks.ContainsKey(_CallbackPos))
3504 Logger.Log("Overwriting an existing ItemsCopiedCallback", Helpers.LogLevel.Warning, Client);
3505  
3506 _ItemCopiedCallbacks[_CallbackPos] = callback;
3507  
3508 return _CallbackPos;
3509 }
3510 }
3511  
3512 /// <summary>
3513 /// Create a CRC from an InventoryItem
3514 /// </summary>
3515 /// <param name="iitem">The source InventoryItem</param>
3516 /// <returns>A uint representing the source InventoryItem as a CRC</returns>
3517 public static uint ItemCRC(InventoryItem iitem)
3518 {
3519 uint CRC = 0;
3520  
3521 // IDs
3522 CRC += iitem.AssetUUID.CRC(); // AssetID
3523 CRC += iitem.ParentUUID.CRC(); // FolderID
3524 CRC += iitem.UUID.CRC(); // ItemID
3525  
3526 // Permission stuff
3527 CRC += iitem.CreatorID.CRC(); // CreatorID
3528 CRC += iitem.OwnerID.CRC(); // OwnerID
3529 CRC += iitem.GroupID.CRC(); // GroupID
3530  
3531 // CRC += another 4 words which always seem to be zero -- unclear if this is a UUID or what
3532 CRC += (uint)iitem.Permissions.OwnerMask; //owner_mask; // Either owner_mask or next_owner_mask may need to be
3533 CRC += (uint)iitem.Permissions.NextOwnerMask; //next_owner_mask; // switched with base_mask -- 2 values go here and in my
3534 CRC += (uint)iitem.Permissions.EveryoneMask; //everyone_mask; // study item, the three were identical.
3535 CRC += (uint)iitem.Permissions.GroupMask; //group_mask;
3536  
3537 // The rest of the CRC fields
3538 CRC += (uint)iitem.Flags; // Flags
3539 CRC += (uint)iitem.InventoryType; // InvType
3540 CRC += (uint)iitem.AssetType; // Type
3541 CRC += (uint)Utils.DateTimeToUnixTime(iitem.CreationDate); // CreationDate
3542 CRC += (uint)iitem.SalePrice; // SalePrice
3543 CRC += (uint)((uint)iitem.SaleType * 0x07073096); // SaleType
3544  
3545 return CRC;
3546 }
3547  
3548 /// <summary>
3549 /// Reverses a cheesy XORing with a fixed UUID to convert a shadow_id to an asset_id
3550 /// </summary>
3551 /// <param name="shadowID">Obfuscated shadow_id value</param>
3552 /// <returns>Deobfuscated asset_id value</returns>
3553 public static UUID DecryptShadowID(UUID shadowID)
3554 {
3555 return shadowID ^ MAGIC_ID;
3556 }
3557  
3558 /// <summary>
3559 /// Does a cheesy XORing with a fixed UUID to convert an asset_id to a shadow_id
3560 /// </summary>
3561 /// <param name="assetID">asset_id value to obfuscate</param>
3562 /// <returns>Obfuscated shadow_id value</returns>
3563 public static UUID EncryptAssetID(UUID assetID)
3564 {
3565 return assetID ^ MAGIC_ID;
3566 }
3567  
3568 /// <summary>
3569 /// Wrapper for creating a new <seealso cref="InventoryItem"/> object
3570 /// </summary>
3571 /// <param name="type">The type of item from the <seealso cref="InventoryType"/> enum</param>
3572 /// <param name="id">The <seealso cref="UUID"/> of the newly created object</param>
3573 /// <returns>An <seealso cref="InventoryItem"/> object with the type and id passed</returns>
3574 public static InventoryItem CreateInventoryItem(InventoryType type, UUID id)
3575 {
3576 switch (type)
3577 {
3578 case InventoryType.Texture: return new InventoryTexture(id);
3579 case InventoryType.Sound: return new InventorySound(id);
3580 case InventoryType.CallingCard: return new InventoryCallingCard(id);
3581 case InventoryType.Landmark: return new InventoryLandmark(id);
3582 case InventoryType.Object: return new InventoryObject(id);
3583 case InventoryType.Notecard: return new InventoryNotecard(id);
3584 case InventoryType.Category: return new InventoryCategory(id);
3585 case InventoryType.LSL: return new InventoryLSL(id);
3586 case InventoryType.Snapshot: return new InventorySnapshot(id);
3587 case InventoryType.Attachment: return new InventoryAttachment(id);
3588 case InventoryType.Wearable: return new InventoryWearable(id);
3589 case InventoryType.Animation: return new InventoryAnimation(id);
3590 case InventoryType.Gesture: return new InventoryGesture(id);
3591 default: return new InventoryItem(type, id);
3592 }
3593 }
3594  
3595 private InventoryItem SafeCreateInventoryItem(InventoryType InvType, UUID ItemID)
3596 {
3597 InventoryItem ret = null;
3598  
3599 if (_Store.Contains(ItemID))
3600 ret = _Store[ItemID] as InventoryItem;
3601  
3602 if (ret == null)
3603 ret = CreateInventoryItem(InvType, ItemID);
3604  
3605 return ret;
3606 }
3607  
3608 private static bool ParseLine(string line, out string key, out string value)
3609 {
3610 // Clean up and convert tabs to spaces
3611 line = line.Trim();
3612 line = line.Replace('\t', ' ');
3613  
3614 // Shrink all whitespace down to single spaces
3615 while (line.IndexOf(" ") > 0)
3616 line = line.Replace(" ", " ");
3617  
3618 if (line.Length > 2)
3619 {
3620 int sep = line.IndexOf(' ');
3621 if (sep > 0)
3622 {
3623 key = line.Substring(0, sep);
3624 value = line.Substring(sep + 1);
3625  
3626 return true;
3627 }
3628 }
3629 else if (line.Length == 1)
3630 {
3631 key = line;
3632 value = String.Empty;
3633 return true;
3634 }
3635  
3636 key = null;
3637 value = null;
3638 return false;
3639 }
3640  
3641 /// <summary>
3642 /// Parse the results of a RequestTaskInventory() response
3643 /// </summary>
3644 /// <param name="taskData">A string which contains the data from the task reply</param>
3645 /// <returns>A List containing the items contained within the tasks inventory</returns>
3646 public static List<InventoryBase> ParseTaskInventory(string taskData)
3647 {
3648 List<InventoryBase> items = new List<InventoryBase>();
3649 int lineNum = 0;
3650 string[] lines = taskData.Replace("\r\n", "\n").Split('\n');
3651  
3652 while (lineNum < lines.Length)
3653 {
3654 string key, value;
3655 if (ParseLine(lines[lineNum++], out key, out value))
3656 {
3657 if (key == "inv_object")
3658 {
3659 #region inv_object
3660  
3661 // In practice this appears to only be used for folders
3662 UUID itemID = UUID.Zero;
3663 UUID parentID = UUID.Zero;
3664 string name = String.Empty;
3665 AssetType assetType = AssetType.Unknown;
3666  
3667 while (lineNum < lines.Length)
3668 {
3669 if (ParseLine(lines[lineNum++], out key, out value))
3670 {
3671 if (key == "{")
3672 {
3673 continue;
3674 }
3675 else if (key == "}")
3676 {
3677 break;
3678 }
3679 else if (key == "obj_id")
3680 {
3681 UUID.TryParse(value, out itemID);
3682 }
3683 else if (key == "parent_id")
3684 {
3685 UUID.TryParse(value, out parentID);
3686 }
3687 else if (key == "type")
3688 {
3689 assetType = Utils.StringToAssetType(value);
3690 }
3691 else if (key == "name")
3692 {
3693 name = value.Substring(0, value.IndexOf('|'));
3694 }
3695 }
3696 }
3697  
3698 if (assetType == AssetType.Folder)
3699 {
3700 InventoryFolder folder = new InventoryFolder(itemID);
3701 folder.Name = name;
3702 folder.ParentUUID = parentID;
3703  
3704 items.Add(folder);
3705 }
3706 else
3707 {
3708 InventoryItem item = new InventoryItem(itemID);
3709 item.Name = name;
3710 item.ParentUUID = parentID;
3711 item.AssetType = assetType;
3712  
3713 items.Add(item);
3714 }
3715  
3716 #endregion inv_object
3717 }
3718 else if (key == "inv_item")
3719 {
3720 #region inv_item
3721  
3722 // Any inventory item that links to an assetID, has permissions, etc
3723 UUID itemID = UUID.Zero;
3724 UUID assetID = UUID.Zero;
3725 UUID parentID = UUID.Zero;
3726 UUID creatorID = UUID.Zero;
3727 UUID ownerID = UUID.Zero;
3728 UUID lastOwnerID = UUID.Zero;
3729 UUID groupID = UUID.Zero;
3730 bool groupOwned = false;
3731 string name = String.Empty;
3732 string desc = String.Empty;
3733 AssetType assetType = AssetType.Unknown;
3734 InventoryType inventoryType = InventoryType.Unknown;
3735 DateTime creationDate = Utils.Epoch;
3736 uint flags = 0;
3737 Permissions perms = Permissions.NoPermissions;
3738 SaleType saleType = SaleType.Not;
3739 int salePrice = 0;
3740  
3741 while (lineNum < lines.Length)
3742 {
3743 if (ParseLine(lines[lineNum++], out key, out value))
3744 {
3745 if (key == "{")
3746 {
3747 continue;
3748 }
3749 else if (key == "}")
3750 {
3751 break;
3752 }
3753 else if (key == "item_id")
3754 {
3755 UUID.TryParse(value, out itemID);
3756 }
3757 else if (key == "parent_id")
3758 {
3759 UUID.TryParse(value, out parentID);
3760 }
3761 else if (key == "permissions")
3762 {
3763 #region permissions
3764  
3765 while (lineNum < lines.Length)
3766 {
3767 if (ParseLine(lines[lineNum++], out key, out value))
3768 {
3769 if (key == "{")
3770 {
3771 continue;
3772 }
3773 else if (key == "}")
3774 {
3775 break;
3776 }
3777 else if (key == "creator_mask")
3778 {
3779 // Deprecated
3780 uint val;
3781 if (Utils.TryParseHex(value, out val))
3782 perms.BaseMask = (PermissionMask)val;
3783 }
3784 else if (key == "base_mask")
3785 {
3786 uint val;
3787 if (Utils.TryParseHex(value, out val))
3788 perms.BaseMask = (PermissionMask)val;
3789 }
3790 else if (key == "owner_mask")
3791 {
3792 uint val;
3793 if (Utils.TryParseHex(value, out val))
3794 perms.OwnerMask = (PermissionMask)val;
3795 }
3796 else if (key == "group_mask")
3797 {
3798 uint val;
3799 if (Utils.TryParseHex(value, out val))
3800 perms.GroupMask = (PermissionMask)val;
3801 }
3802 else if (key == "everyone_mask")
3803 {
3804 uint val;
3805 if (Utils.TryParseHex(value, out val))
3806 perms.EveryoneMask = (PermissionMask)val;
3807 }
3808 else if (key == "next_owner_mask")
3809 {
3810 uint val;
3811 if (Utils.TryParseHex(value, out val))
3812 perms.NextOwnerMask = (PermissionMask)val;
3813 }
3814 else if (key == "creator_id")
3815 {
3816  
3817 UUID.TryParse(value, out creatorID);
3818 }
3819 else if (key == "owner_id")
3820 {
3821 UUID.TryParse(value, out ownerID);
3822 }
3823 else if (key == "last_owner_id")
3824 {
3825 UUID.TryParse(value, out lastOwnerID);
3826 }
3827 else if (key == "group_id")
3828 {
3829 UUID.TryParse(value, out groupID);
3830 }
3831 else if (key == "group_owned")
3832 {
3833 uint val;
3834 if (UInt32.TryParse(value, out val))
3835 groupOwned = (val != 0);
3836 }
3837 }
3838 }
3839  
3840 #endregion permissions
3841 }
3842 else if (key == "sale_info")
3843 {
3844 #region sale_info
3845  
3846 while (lineNum < lines.Length)
3847 {
3848 if (ParseLine(lines[lineNum++], out key, out value))
3849 {
3850 if (key == "{")
3851 {
3852 continue;
3853 }
3854 else if (key == "}")
3855 {
3856 break;
3857 }
3858 else if (key == "sale_type")
3859 {
3860 saleType = Utils.StringToSaleType(value);
3861 }
3862 else if (key == "sale_price")
3863 {
3864 Int32.TryParse(value, out salePrice);
3865 }
3866 }
3867 }
3868  
3869 #endregion sale_info
3870 }
3871 else if (key == "shadow_id")
3872 {
3873 UUID shadowID;
3874 if (UUID.TryParse(value, out shadowID))
3875 assetID = DecryptShadowID(shadowID);
3876 }
3877 else if (key == "asset_id")
3878 {
3879 UUID.TryParse(value, out assetID);
3880 }
3881 else if (key == "type")
3882 {
3883 assetType = Utils.StringToAssetType(value);
3884 }
3885 else if (key == "inv_type")
3886 {
3887 inventoryType = Utils.StringToInventoryType(value);
3888 }
3889 else if (key == "flags")
3890 {
3891 UInt32.TryParse(value, out flags);
3892 }
3893 else if (key == "name")
3894 {
3895 name = value.Substring(0, value.IndexOf('|'));
3896 }
3897 else if (key == "desc")
3898 {
3899 desc = value.Substring(0, value.IndexOf('|'));
3900 }
3901 else if (key == "creation_date")
3902 {
3903 uint timestamp;
3904 if (UInt32.TryParse(value, out timestamp))
3905 creationDate = Utils.UnixTimeToDateTime(timestamp);
3906 else
3907 Logger.Log("Failed to parse creation_date " + value, Helpers.LogLevel.Warning);
3908 }
3909 }
3910 }
3911  
3912 InventoryItem item = CreateInventoryItem(inventoryType, itemID);
3913 item.AssetUUID = assetID;
3914 item.AssetType = assetType;
3915 item.CreationDate = creationDate;
3916 item.CreatorID = creatorID;
3917 item.Description = desc;
3918 item.Flags = flags;
3919 item.GroupID = groupID;
3920 item.GroupOwned = groupOwned;
3921 item.Name = name;
3922 item.OwnerID = ownerID;
3923 item.LastOwnerID = lastOwnerID;
3924 item.ParentUUID = parentID;
3925 item.Permissions = perms;
3926 item.SalePrice = salePrice;
3927 item.SaleType = saleType;
3928  
3929 items.Add(item);
3930  
3931 #endregion inv_item
3932 }
3933 else
3934 {
3935 Logger.Log("Unrecognized token " + key + " in: " + Environment.NewLine + taskData,
3936 Helpers.LogLevel.Error);
3937 }
3938 }
3939 }
3940  
3941 return items;
3942 }
3943  
3944 #endregion Helper Functions
3945  
3946 #region Internal Callbacks
3947  
3948 void Self_IM(object sender, InstantMessageEventArgs e)
3949 {
3950 // TODO: MainAvatar.InstantMessageDialog.GroupNotice can also be an inventory offer, should we
3951 // handle it here?
3952  
3953 if (m_InventoryObjectOffered != null &&
3954 (e.IM.Dialog == InstantMessageDialog.InventoryOffered
3955 || e.IM.Dialog == InstantMessageDialog.TaskInventoryOffered))
3956 {
3957 AssetType type = AssetType.Unknown;
3958 UUID objectID = UUID.Zero;
3959 bool fromTask = false;
3960  
3961 if (e.IM.Dialog == InstantMessageDialog.InventoryOffered)
3962 {
3963 if (e.IM.BinaryBucket.Length == 17)
3964 {
3965 type = (AssetType)e.IM.BinaryBucket[0];
3966 objectID = new UUID(e.IM.BinaryBucket, 1);
3967 fromTask = false;
3968 }
3969 else
3970 {
3971 Logger.Log("Malformed inventory offer from agent", Helpers.LogLevel.Warning, Client);
3972 return;
3973 }
3974 }
3975 else if (e.IM.Dialog == InstantMessageDialog.TaskInventoryOffered)
3976 {
3977 if (e.IM.BinaryBucket.Length == 1)
3978 {
3979 type = (AssetType)e.IM.BinaryBucket[0];
3980 fromTask = true;
3981 }
3982 else
3983 {
3984 Logger.Log("Malformed inventory offer from object", Helpers.LogLevel.Warning, Client);
3985 return;
3986 }
3987 }
3988  
3989 // Find the folder where this is going to go
3990 UUID destinationFolderID = FindFolderForType(type);
3991  
3992 // Fire the callback
3993 try
3994 {
3995 ImprovedInstantMessagePacket imp = new ImprovedInstantMessagePacket();
3996 imp.AgentData.AgentID = Client.Self.AgentID;
3997 imp.AgentData.SessionID = Client.Self.SessionID;
3998 imp.MessageBlock.FromGroup = false;
3999 imp.MessageBlock.ToAgentID = e.IM.FromAgentID;
4000 imp.MessageBlock.Offline = 0;
4001 imp.MessageBlock.ID = e.IM.IMSessionID;
4002 imp.MessageBlock.Timestamp = 0;
4003 imp.MessageBlock.FromAgentName = Utils.StringToBytes(Client.Self.Name);
4004 imp.MessageBlock.Message = Utils.EmptyBytes;
4005 imp.MessageBlock.ParentEstateID = 0;
4006 imp.MessageBlock.RegionID = UUID.Zero;
4007 imp.MessageBlock.Position = Client.Self.SimPosition;
4008  
4009 InventoryObjectOfferedEventArgs args = new InventoryObjectOfferedEventArgs(e.IM, type, objectID, fromTask, destinationFolderID);
4010  
4011 OnInventoryObjectOffered(args);
4012  
4013 if (args.Accept)
4014 {
4015 // Accept the inventory offer
4016 switch (e.IM.Dialog)
4017 {
4018 case InstantMessageDialog.InventoryOffered:
4019 imp.MessageBlock.Dialog = (byte)InstantMessageDialog.InventoryAccepted;
4020 break;
4021 case InstantMessageDialog.TaskInventoryOffered:
4022 imp.MessageBlock.Dialog = (byte)InstantMessageDialog.TaskInventoryAccepted;
4023 break;
4024 case InstantMessageDialog.GroupNotice:
4025 imp.MessageBlock.Dialog = (byte)InstantMessageDialog.GroupNoticeInventoryAccepted;
4026 break;
4027 }
4028 imp.MessageBlock.BinaryBucket = args.FolderID.GetBytes();
4029 }
4030 else
4031 {
4032 // Decline the inventory offer
4033 switch (e.IM.Dialog)
4034 {
4035 case InstantMessageDialog.InventoryOffered:
4036 imp.MessageBlock.Dialog = (byte)InstantMessageDialog.InventoryDeclined;
4037 break;
4038 case InstantMessageDialog.TaskInventoryOffered:
4039 imp.MessageBlock.Dialog = (byte)InstantMessageDialog.TaskInventoryDeclined;
4040 break;
4041 case InstantMessageDialog.GroupNotice:
4042 imp.MessageBlock.Dialog = (byte)InstantMessageDialog.GroupNoticeInventoryDeclined;
4043 break;
4044 }
4045  
4046 imp.MessageBlock.BinaryBucket = Utils.EmptyBytes;
4047 }
4048  
4049 Client.Network.SendPacket(imp, e.Simulator);
4050 }
4051 catch (Exception ex)
4052 {
4053 Logger.Log(ex.Message, Helpers.LogLevel.Error, Client, ex);
4054 }
4055 }
4056 }
4057  
4058 private void CreateItemFromAssetResponse(CapsClient client, OSD result, Exception error)
4059 {
4060 object[] args = (object[])client.UserData;
4061 ItemCreatedFromAssetCallback callback = (ItemCreatedFromAssetCallback)args[0];
4062 byte[] itemData = (byte[])args[1];
4063 int millisecondsTimeout = (int)args[2];
4064 OSDMap request = (OSDMap)args[3];
4065  
4066 if (result == null)
4067 {
4068 try { callback(false, error.Message, UUID.Zero, UUID.Zero); }
4069 catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); }
4070 return;
4071 }
4072  
4073 if (result.Type == OSDType.Unknown)
4074 {
4075 try
4076 {
4077 callback(false, "Failed to parse asset and item UUIDs", UUID.Zero, UUID.Zero);
4078 }
4079 catch (Exception e)
4080 {
4081 Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e);
4082 }
4083 }
4084  
4085 OSDMap contents = (OSDMap)result;
4086  
4087 string status = contents["state"].AsString().ToLower();
4088  
4089 if (status == "upload")
4090 {
4091 string uploadURL = contents["uploader"].AsString();
4092  
4093 Logger.DebugLog("CreateItemFromAsset: uploading to " + uploadURL);
4094  
4095 // This makes the assumption that all uploads go to CurrentSim, to avoid
4096 // the problem of HttpRequestState not knowing anything about simulators
4097 CapsClient upload = new CapsClient(new Uri(uploadURL));
4098 upload.OnComplete += CreateItemFromAssetResponse;
4099 upload.UserData = new object[] { callback, itemData, millisecondsTimeout, request };
4100 upload.BeginGetResponse(itemData, "application/octet-stream", millisecondsTimeout);
4101 }
4102 else if (status == "complete")
4103 {
4104 Logger.DebugLog("CreateItemFromAsset: completed");
4105  
4106 if (contents.ContainsKey("new_inventory_item") && contents.ContainsKey("new_asset"))
4107 {
4108 // Request full update on the item in order to update the local store
4109 RequestFetchInventory(contents["new_inventory_item"].AsUUID(), Client.Self.AgentID);
4110  
4111 try { callback(true, String.Empty, contents["new_inventory_item"].AsUUID(), contents["new_asset"].AsUUID()); }
4112 catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); }
4113 }
4114 else
4115 {
4116 try { callback(false, "Failed to parse asset and item UUIDs", UUID.Zero, UUID.Zero); }
4117 catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); }
4118 }
4119 }
4120 else
4121 {
4122 // Failure
4123 try { callback(false, status, UUID.Zero, UUID.Zero); }
4124 catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); }
4125 }
4126 }
4127  
4128  
4129 private void Network_OnLoginResponse(bool loginSuccess, bool redirect, string message, string reason, LoginResponseData replyData)
4130 {
4131 if (loginSuccess)
4132 {
4133 // Initialize the store here so we know who owns it:
4134 _Store = new Inventory(Client, this, Client.Self.AgentID);
4135 Logger.DebugLog("Setting InventoryRoot to " + replyData.InventoryRoot.ToString(), Client);
4136 InventoryFolder rootFolder = new InventoryFolder(replyData.InventoryRoot);
4137 rootFolder.Name = String.Empty;
4138 rootFolder.ParentUUID = UUID.Zero;
4139 _Store.RootFolder = rootFolder;
4140  
4141 for (int i = 0; i < replyData.InventorySkeleton.Length; i++)
4142 _Store.UpdateNodeFor(replyData.InventorySkeleton[i]);
4143  
4144 InventoryFolder libraryRootFolder = new InventoryFolder(replyData.LibraryRoot);
4145 libraryRootFolder.Name = String.Empty;
4146 libraryRootFolder.ParentUUID = UUID.Zero;
4147 _Store.LibraryFolder = libraryRootFolder;
4148  
4149 for (int i = 0; i < replyData.LibrarySkeleton.Length; i++)
4150 _Store.UpdateNodeFor(replyData.LibrarySkeleton[i]);
4151 }
4152 }
4153  
4154 private void UploadInventoryAssetResponse(CapsClient client, OSD result, Exception error)
4155 {
4156 OSDMap contents = result as OSDMap;
4157 KeyValuePair<InventoryUploadedAssetCallback, byte[]> kvp = (KeyValuePair<InventoryUploadedAssetCallback, byte[]>)(((object[])client.UserData)[0]);
4158 InventoryUploadedAssetCallback callback = kvp.Key;
4159 byte[] itemData = (byte[])kvp.Value;
4160  
4161 if (error == null && contents != null)
4162 {
4163 string status = contents["state"].AsString();
4164  
4165 if (status == "upload")
4166 {
4167 Uri uploadURL = contents["uploader"].AsUri();
4168  
4169 if (uploadURL != null)
4170 {
4171 // This makes the assumption that all uploads go to CurrentSim, to avoid
4172 // the problem of HttpRequestState not knowing anything about simulators
4173 CapsClient upload = new CapsClient(uploadURL);
4174 upload.OnComplete += UploadInventoryAssetResponse;
4175 upload.UserData = new object[2] { kvp, (UUID)(((object[])client.UserData)[1]) };
4176 upload.BeginGetResponse(itemData, "application/octet-stream", Client.Settings.CAPS_TIMEOUT);
4177 }
4178 else
4179 {
4180 try { callback(false, "Missing uploader URL", UUID.Zero, UUID.Zero); }
4181 catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); }
4182 }
4183 }
4184 else if (status == "complete")
4185 {
4186 if (contents.ContainsKey("new_asset"))
4187 {
4188 // Request full item update so we keep store in sync
4189 RequestFetchInventory((UUID)(((object[])client.UserData)[1]), contents["new_asset"].AsUUID());
4190  
4191 try { callback(true, String.Empty, (UUID)(((object[])client.UserData)[1]), contents["new_asset"].AsUUID()); }
4192 catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); }
4193 }
4194 else
4195 {
4196 try { callback(false, "Failed to parse asset and item UUIDs", UUID.Zero, UUID.Zero); }
4197 catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); }
4198 }
4199 }
4200 else
4201 {
4202 try { callback(false, status, UUID.Zero, UUID.Zero); }
4203 catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); }
4204 }
4205 }
4206 else
4207 {
4208 string message = "Unrecognized or empty response";
4209  
4210 if (error != null)
4211 {
4212 if (error is WebException)
4213 message = ((HttpWebResponse)((WebException)error).Response).StatusDescription;
4214  
4215 if (message == null || message == "None")
4216 message = error.Message;
4217 }
4218  
4219 try { callback(false, message, UUID.Zero, UUID.Zero); }
4220 catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); }
4221 }
4222 }
4223  
4224 private void UpdateScriptAgentInventoryResponse(CapsClient client, OSD result, Exception error)
4225 {
4226 KeyValuePair<ScriptUpdatedCallback, byte[]> kvp = (KeyValuePair<ScriptUpdatedCallback, byte[]>)(((object[])client.UserData)[0]);
4227 ScriptUpdatedCallback callback = kvp.Key;
4228 byte[] itemData = (byte[])kvp.Value;
4229  
4230 if (result == null)
4231 {
4232 try { callback(false, error.Message, false, null, UUID.Zero, UUID.Zero); }
4233 catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); }
4234 return;
4235 }
4236  
4237 OSDMap contents = (OSDMap)result;
4238  
4239 string status = contents["state"].AsString();
4240 if (status == "upload")
4241 {
4242 string uploadURL = contents["uploader"].AsString();
4243  
4244 CapsClient upload = new CapsClient(new Uri(uploadURL));
4245 upload.OnComplete += new CapsClient.CompleteCallback(UpdateScriptAgentInventoryResponse);
4246 upload.UserData = new object[2] { kvp, (UUID)(((object[])client.UserData)[1]) };
4247 upload.BeginGetResponse(itemData, "application/octet-stream", Client.Settings.CAPS_TIMEOUT);
4248 }
4249 else if (status == "complete" && callback != null)
4250 {
4251 if (contents.ContainsKey("new_asset"))
4252 {
4253 // Request full item update so we keep store in sync
4254 RequestFetchInventory((UUID)(((object[])client.UserData)[1]), contents["new_asset"].AsUUID());
4255  
4256  
4257 try
4258 {
4259 List<string> compileErrors = null;
4260  
4261 if (contents.ContainsKey("errors"))
4262 {
4263 OSDArray errors = (OSDArray)contents["errors"];
4264 compileErrors = new List<string>(errors.Count);
4265  
4266 for (int i = 0; i < errors.Count; i++)
4267 {
4268 compileErrors.Add(errors[i].AsString());
4269 }
4270 }
4271  
4272 callback(true,
4273 status,
4274 contents["compiled"].AsBoolean(),
4275 compileErrors,
4276 (UUID)(((object[])client.UserData)[1]),
4277 contents["new_asset"].AsUUID());
4278 }
4279 catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); }
4280 }
4281 else
4282 {
4283 try { callback(false, "Failed to parse asset UUID", false, null, UUID.Zero, UUID.Zero); }
4284 catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); }
4285 }
4286 }
4287 else if (callback != null)
4288 {
4289 try { callback(false, status, false, null, UUID.Zero, UUID.Zero); }
4290 catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); }
4291 }
4292 }
4293 #endregion Internal Handlers
4294  
4295 #region Packet Handlers
4296  
4297 /// <summary>Process an incoming packet and raise the appropriate events</summary>
4298 /// <param name="sender">The sender</param>
4299 /// <param name="e">The EventArgs object containing the packet data</param>
4300 protected void SaveAssetIntoInventoryHandler(object sender, PacketReceivedEventArgs e)
4301 {
4302 if (m_SaveAssetToInventory != null)
4303 {
4304 Packet packet = e.Packet;
4305  
4306 SaveAssetIntoInventoryPacket save = (SaveAssetIntoInventoryPacket)packet;
4307 OnSaveAssetToInventory(new SaveAssetToInventoryEventArgs(save.InventoryData.ItemID, save.InventoryData.NewAssetID));
4308 }
4309 }
4310  
4311 /// <summary>Process an incoming packet and raise the appropriate events</summary>
4312 /// <param name="sender">The sender</param>
4313 /// <param name="e">The EventArgs object containing the packet data</param>
4314 protected void InventoryDescendentsHandler(object sender, PacketReceivedEventArgs e)
4315 {
4316 Packet packet = e.Packet;
4317  
4318 InventoryDescendentsPacket reply = (InventoryDescendentsPacket)packet;
4319  
4320 if (reply.AgentData.Descendents > 0)
4321 {
4322 // InventoryDescendantsReply sends a null folder if the parent doesnt contain any folders
4323 if (reply.FolderData[0].FolderID != UUID.Zero)
4324 {
4325 // Iterate folders in this packet
4326 for (int i = 0; i < reply.FolderData.Length; i++)
4327 {
4328 // If folder already exists then ignore, we assume the version cache
4329 // logic is working and if the folder is stale then it should not be present.
4330  
4331 if (!_Store.Contains(reply.FolderData[i].FolderID))
4332 {
4333 InventoryFolder folder = new InventoryFolder(reply.FolderData[i].FolderID);
4334 folder.ParentUUID = reply.FolderData[i].ParentID;
4335 folder.Name = Utils.BytesToString(reply.FolderData[i].Name);
4336 folder.PreferredType = (AssetType)reply.FolderData[i].Type;
4337 folder.OwnerID = reply.AgentData.OwnerID;
4338  
4339 _Store[folder.UUID] = folder;
4340 }
4341 }
4342 }
4343  
4344 // InventoryDescendantsReply sends a null item if the parent doesnt contain any items.
4345 if (reply.ItemData[0].ItemID != UUID.Zero)
4346 {
4347 // Iterate items in this packet
4348 for (int i = 0; i < reply.ItemData.Length; i++)
4349 {
4350 if (reply.ItemData[i].ItemID != UUID.Zero)
4351 {
4352 InventoryItem item;
4353 /*
4354 * Objects that have been attached in-world prior to being stored on the
4355 * asset server are stored with the InventoryType of 0 (Texture)
4356 * instead of 17 (Attachment)
4357 *
4358 * This corrects that behavior by forcing Object Asset types that have an
4359 * invalid InventoryType with the proper InventoryType of Attachment.
4360 */
4361 if ((AssetType)reply.ItemData[i].Type == AssetType.Object
4362 && (InventoryType)reply.ItemData[i].InvType == InventoryType.Texture)
4363 {
4364 item = CreateInventoryItem(InventoryType.Attachment, reply.ItemData[i].ItemID);
4365 item.InventoryType = InventoryType.Attachment;
4366 }
4367 else
4368 {
4369 item = CreateInventoryItem((InventoryType)reply.ItemData[i].InvType, reply.ItemData[i].ItemID);
4370 item.InventoryType = (InventoryType)reply.ItemData[i].InvType;
4371 }
4372  
4373 item.ParentUUID = reply.ItemData[i].FolderID;
4374 item.CreatorID = reply.ItemData[i].CreatorID;
4375 item.AssetType = (AssetType)reply.ItemData[i].Type;
4376 item.AssetUUID = reply.ItemData[i].AssetID;
4377 item.CreationDate = Utils.UnixTimeToDateTime((uint)reply.ItemData[i].CreationDate);
4378 item.Description = Utils.BytesToString(reply.ItemData[i].Description);
4379 item.Flags = reply.ItemData[i].Flags;
4380 item.Name = Utils.BytesToString(reply.ItemData[i].Name);
4381 item.GroupID = reply.ItemData[i].GroupID;
4382 item.GroupOwned = reply.ItemData[i].GroupOwned;
4383 item.Permissions = new Permissions(
4384 reply.ItemData[i].BaseMask,
4385 reply.ItemData[i].EveryoneMask,
4386 reply.ItemData[i].GroupMask,
4387 reply.ItemData[i].NextOwnerMask,
4388 reply.ItemData[i].OwnerMask);
4389 item.SalePrice = reply.ItemData[i].SalePrice;
4390 item.SaleType = (SaleType)reply.ItemData[i].SaleType;
4391 item.OwnerID = reply.AgentData.OwnerID;
4392  
4393 _Store[item.UUID] = item;
4394 }
4395 }
4396 }
4397 }
4398  
4399 InventoryFolder parentFolder = null;
4400  
4401 if (_Store.Contains(reply.AgentData.FolderID) &&
4402 _Store[reply.AgentData.FolderID] is InventoryFolder)
4403 {
4404 parentFolder = _Store[reply.AgentData.FolderID] as InventoryFolder;
4405 }
4406 else
4407 {
4408 Logger.Log("Don't have a reference to FolderID " + reply.AgentData.FolderID.ToString() +
4409 " or it is not a folder", Helpers.LogLevel.Error, Client);
4410 return;
4411 }
4412  
4413 if (reply.AgentData.Version < parentFolder.Version)
4414 {
4415 Logger.Log("Got an outdated InventoryDescendents packet for folder " + parentFolder.Name +
4416 ", this version = " + reply.AgentData.Version + ", latest version = " + parentFolder.Version,
4417 Helpers.LogLevel.Warning, Client);
4418 return;
4419 }
4420  
4421 parentFolder.Version = reply.AgentData.Version;
4422 // FIXME: reply.AgentData.Descendants is not parentFolder.DescendentCount if we didn't
4423 // request items and folders
4424 parentFolder.DescendentCount = reply.AgentData.Descendents;
4425 _Store.GetNodeFor(reply.AgentData.FolderID).NeedsUpdate = false;
4426  
4427 #region FindObjectsByPath Handling
4428  
4429 if (_Searches.Count > 0)
4430 {
4431 lock (_Searches)
4432 {
4433 StartSearch:
4434  
4435 // Iterate over all of the outstanding searches
4436 for (int i = 0; i < _Searches.Count; i++)
4437 {
4438 InventorySearch search = _Searches[i];
4439 List<InventoryBase> folderContents = _Store.GetContents(search.Folder);
4440  
4441 // Iterate over all of the inventory objects in the base search folder
4442 for (int j = 0; j < folderContents.Count; j++)
4443 {
4444 // Check if this inventory object matches the current path node
4445 if (folderContents[j].Name == search.Path[search.Level])
4446 {
4447 if (search.Level == search.Path.Length - 1)
4448 {
4449 Logger.DebugLog("Finished path search of " + String.Join("/", search.Path), Client);
4450  
4451 // This is the last node in the path, fire the callback and clean up
4452 if (m_FindObjectByPathReply != null)
4453 {
4454 OnFindObjectByPathReply(new FindObjectByPathReplyEventArgs(String.Join("/", search.Path),
4455 folderContents[j].UUID));
4456 }
4457  
4458 // Remove this entry and restart the loop since we are changing the collection size
4459 _Searches.RemoveAt(i);
4460 goto StartSearch;
4461 }
4462 else
4463 {
4464 // We found a match but it is not the end of the path, request the next level
4465 Logger.DebugLog(String.Format("Matched level {0}/{1} in a path search of {2}",
4466 search.Level, search.Path.Length - 1, String.Join("/", search.Path)), Client);
4467  
4468 search.Folder = folderContents[j].UUID;
4469 search.Level++;
4470 _Searches[i] = search;
4471  
4472 RequestFolderContents(search.Folder, search.Owner, true, true,
4473 InventorySortOrder.ByName);
4474 }
4475 }
4476 }
4477 }
4478 }
4479 }
4480  
4481 #endregion FindObjectsByPath Handling
4482  
4483 // Callback for inventory folder contents being updated
4484 OnFolderUpdated(new FolderUpdatedEventArgs(parentFolder.UUID, true));
4485 }
4486  
4487 /// <summary>
4488 /// UpdateCreateInventoryItem packets are received when a new inventory item
4489 /// is created. This may occur when an object that's rezzed in world is
4490 /// taken into inventory, when an item is created using the CreateInventoryItem
4491 /// packet, or when an object is purchased
4492 /// </summary>
4493 /// <param name="sender">The sender</param>
4494 /// <param name="e">The EventArgs object containing the packet data</param>
4495 protected void UpdateCreateInventoryItemHandler(object sender, PacketReceivedEventArgs e)
4496 {
4497 Packet packet = e.Packet;
4498  
4499 UpdateCreateInventoryItemPacket reply = packet as UpdateCreateInventoryItemPacket;
4500  
4501 foreach (UpdateCreateInventoryItemPacket.InventoryDataBlock dataBlock in reply.InventoryData)
4502 {
4503 if (dataBlock.InvType == (sbyte)InventoryType.Folder)
4504 {
4505 Logger.Log("Received InventoryFolder in an UpdateCreateInventoryItem packet, this should not happen!",
4506 Helpers.LogLevel.Error, Client);
4507 continue;
4508 }
4509  
4510 InventoryItem item = CreateInventoryItem((InventoryType)dataBlock.InvType, dataBlock.ItemID);
4511 item.AssetType = (AssetType)dataBlock.Type;
4512 item.AssetUUID = dataBlock.AssetID;
4513 item.CreationDate = Utils.UnixTimeToDateTime(dataBlock.CreationDate);
4514 item.CreatorID = dataBlock.CreatorID;
4515 item.Description = Utils.BytesToString(dataBlock.Description);
4516 item.Flags = dataBlock.Flags;
4517 item.GroupID = dataBlock.GroupID;
4518 item.GroupOwned = dataBlock.GroupOwned;
4519 item.Name = Utils.BytesToString(dataBlock.Name);
4520 item.OwnerID = dataBlock.OwnerID;
4521 item.ParentUUID = dataBlock.FolderID;
4522 item.Permissions = new Permissions(
4523 dataBlock.BaseMask,
4524 dataBlock.EveryoneMask,
4525 dataBlock.GroupMask,
4526 dataBlock.NextOwnerMask,
4527 dataBlock.OwnerMask);
4528 item.SalePrice = dataBlock.SalePrice;
4529 item.SaleType = (SaleType)dataBlock.SaleType;
4530  
4531 /*
4532 * When attaching new objects, an UpdateCreateInventoryItem packet will be
4533 * returned by the server that has a FolderID/ParentUUID of zero. It is up
4534 * to the client to make sure that the item gets a good folder, otherwise
4535 * it will end up inaccesible in inventory.
4536 */
4537 if (item.ParentUUID == UUID.Zero)
4538 {
4539 // assign default folder for type
4540 item.ParentUUID = FindFolderForType(item.AssetType);
4541  
4542 Logger.Log("Received an item through UpdateCreateInventoryItem with no parent folder, assigning to folder " +
4543 item.ParentUUID, Helpers.LogLevel.Info);
4544  
4545 // send update to the sim
4546 RequestUpdateItem(item);
4547 }
4548  
4549 // Update the local copy
4550 _Store[item.UUID] = item;
4551  
4552 // Look for an "item created" callback
4553 ItemCreatedCallback createdCallback;
4554 if (_ItemCreatedCallbacks.TryGetValue(dataBlock.CallbackID, out createdCallback))
4555 {
4556 _ItemCreatedCallbacks.Remove(dataBlock.CallbackID);
4557  
4558 try { createdCallback(true, item); }
4559 catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, Client, ex); }
4560 }
4561  
4562 // TODO: Is this callback even triggered when items are copied?
4563 // Look for an "item copied" callback
4564 ItemCopiedCallback copyCallback;
4565 if (_ItemCopiedCallbacks.TryGetValue(dataBlock.CallbackID, out copyCallback))
4566 {
4567 _ItemCopiedCallbacks.Remove(dataBlock.CallbackID);
4568  
4569 try { copyCallback(item); }
4570 catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, Client, ex); }
4571 }
4572  
4573 //This is triggered when an item is received from a task
4574 if (m_TaskItemReceived != null)
4575 {
4576 OnTaskItemReceived(new TaskItemReceivedEventArgs(item.UUID, dataBlock.FolderID,
4577 item.CreatorID, item.AssetUUID, item.InventoryType));
4578 }
4579 }
4580 }
4581  
4582 /// <summary>Process an incoming packet and raise the appropriate events</summary>
4583 /// <param name="sender">The sender</param>
4584 /// <param name="e">The EventArgs object containing the packet data</param>
4585 protected void MoveInventoryItemHandler(object sender, PacketReceivedEventArgs e)
4586 {
4587 Packet packet = e.Packet;
4588  
4589 MoveInventoryItemPacket move = (MoveInventoryItemPacket)packet;
4590  
4591 for (int i = 0; i < move.InventoryData.Length; i++)
4592 {
4593 // FIXME: Do something here
4594 string newName = Utils.BytesToString(move.InventoryData[i].NewName);
4595  
4596 Logger.Log(String.Format(
4597 "MoveInventoryItemHandler: Item {0} is moving to Folder {1} with new name \"{2}\". Someone write this function!",
4598 move.InventoryData[i].ItemID.ToString(), move.InventoryData[i].FolderID.ToString(),
4599 newName), Helpers.LogLevel.Warning, Client);
4600 }
4601 }
4602  
4603 protected void BulkUpdateInventoryCapHandler(string capsKey, Interfaces.IMessage message, Simulator simulator)
4604 {
4605 BulkUpdateInventoryMessage msg = (BulkUpdateInventoryMessage)message;
4606  
4607 foreach (BulkUpdateInventoryMessage.FolderDataInfo newFolder in msg.FolderData)
4608 {
4609 if (newFolder.FolderID == UUID.Zero) continue;
4610  
4611 InventoryFolder folder;
4612 if (!_Store.Contains(newFolder.FolderID))
4613 {
4614 folder = new InventoryFolder(newFolder.FolderID);
4615 }
4616 else
4617 {
4618 folder = (InventoryFolder)_Store[newFolder.FolderID];
4619 }
4620  
4621 folder.Name = newFolder.Name;
4622 folder.ParentUUID = newFolder.ParentID;
4623 folder.PreferredType = newFolder.Type;
4624 _Store[folder.UUID] = folder;
4625 }
4626  
4627 foreach (BulkUpdateInventoryMessage.ItemDataInfo newItem in msg.ItemData)
4628 {
4629 if (newItem.ItemID == UUID.Zero) continue;
4630 InventoryType invType = newItem.InvType;
4631  
4632 lock (_ItemInventoryTypeRequest)
4633 {
4634 InventoryType storedType = 0;
4635 if (_ItemInventoryTypeRequest.TryGetValue(newItem.CallbackID, out storedType))
4636 {
4637 _ItemInventoryTypeRequest.Remove(newItem.CallbackID);
4638 invType = storedType;
4639 }
4640 }
4641 InventoryItem item = SafeCreateInventoryItem(invType, newItem.ItemID);
4642  
4643 item.AssetType = newItem.Type;
4644 item.AssetUUID = newItem.AssetID;
4645 item.CreationDate = newItem.CreationDate;
4646 item.CreatorID = newItem.CreatorID;
4647 item.Description = newItem.Description;
4648 item.Flags = newItem.Flags;
4649 item.GroupID = newItem.GroupID;
4650 item.GroupOwned = newItem.GroupOwned;
4651 item.Name = newItem.Name;
4652 item.OwnerID = newItem.OwnerID;
4653 item.ParentUUID = newItem.FolderID;
4654 item.Permissions.BaseMask = newItem.BaseMask;
4655 item.Permissions.EveryoneMask = newItem.EveryoneMask;
4656 item.Permissions.GroupMask = newItem.GroupMask;
4657 item.Permissions.NextOwnerMask = newItem.NextOwnerMask;
4658 item.Permissions.OwnerMask = newItem.OwnerMask;
4659 item.SalePrice = newItem.SalePrice;
4660 item.SaleType = newItem.SaleType;
4661  
4662 _Store[item.UUID] = item;
4663  
4664 // Look for an "item created" callback
4665 ItemCreatedCallback callback;
4666 if (_ItemCreatedCallbacks.TryGetValue(newItem.CallbackID, out callback))
4667 {
4668 _ItemCreatedCallbacks.Remove(newItem.CallbackID);
4669  
4670 try { callback(true, item); }
4671 catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, Client, ex); }
4672 }
4673  
4674 // Look for an "item copied" callback
4675 ItemCopiedCallback copyCallback;
4676 if (_ItemCopiedCallbacks.TryGetValue(newItem.CallbackID, out copyCallback))
4677 {
4678 _ItemCopiedCallbacks.Remove(newItem.CallbackID);
4679  
4680 try { copyCallback(item); }
4681 catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, Client, ex); }
4682 }
4683  
4684 }
4685  
4686 }
4687  
4688 /// <summary>Process an incoming packet and raise the appropriate events</summary>
4689 /// <param name="sender">The sender</param>
4690 /// <param name="e">The EventArgs object containing the packet data</param>
4691 protected void BulkUpdateInventoryHandler(object sender, PacketReceivedEventArgs e)
4692 {
4693 Packet packet = e.Packet;
4694  
4695 BulkUpdateInventoryPacket update = packet as BulkUpdateInventoryPacket;
4696  
4697 if (update.FolderData.Length > 0 && update.FolderData[0].FolderID != UUID.Zero)
4698 {
4699 foreach (BulkUpdateInventoryPacket.FolderDataBlock dataBlock in update.FolderData)
4700 {
4701 InventoryFolder folder;
4702 if (!_Store.Contains(dataBlock.FolderID))
4703 {
4704 folder = new InventoryFolder(dataBlock.FolderID);
4705 }
4706 else
4707 {
4708 folder = (InventoryFolder)_Store[dataBlock.FolderID];
4709 }
4710  
4711 if (dataBlock.Name != null)
4712 {
4713 folder.Name = Utils.BytesToString(dataBlock.Name);
4714 }
4715 folder.OwnerID = update.AgentData.AgentID;
4716 folder.ParentUUID = dataBlock.ParentID;
4717 _Store[folder.UUID] = folder;
4718 }
4719 }
4720  
4721 if (update.ItemData.Length > 0 && update.ItemData[0].ItemID != UUID.Zero)
4722 {
4723 for (int i = 0; i < update.ItemData.Length; i++)
4724 {
4725 BulkUpdateInventoryPacket.ItemDataBlock dataBlock = update.ItemData[i];
4726  
4727 InventoryItem item = SafeCreateInventoryItem((InventoryType)dataBlock.InvType, dataBlock.ItemID);
4728  
4729 item.AssetType = (AssetType)dataBlock.Type;
4730 if (dataBlock.AssetID != UUID.Zero) item.AssetUUID = dataBlock.AssetID;
4731 item.CreationDate = Utils.UnixTimeToDateTime(dataBlock.CreationDate);
4732 item.CreatorID = dataBlock.CreatorID;
4733 item.Description = Utils.BytesToString(dataBlock.Description);
4734 item.Flags = dataBlock.Flags;
4735 item.GroupID = dataBlock.GroupID;
4736 item.GroupOwned = dataBlock.GroupOwned;
4737 item.Name = Utils.BytesToString(dataBlock.Name);
4738 item.OwnerID = dataBlock.OwnerID;
4739 item.ParentUUID = dataBlock.FolderID;
4740 item.Permissions = new Permissions(
4741 dataBlock.BaseMask,
4742 dataBlock.EveryoneMask,
4743 dataBlock.GroupMask,
4744 dataBlock.NextOwnerMask,
4745 dataBlock.OwnerMask);
4746 item.SalePrice = dataBlock.SalePrice;
4747 item.SaleType = (SaleType)dataBlock.SaleType;
4748  
4749 _Store[item.UUID] = item;
4750  
4751 // Look for an "item created" callback
4752 ItemCreatedCallback callback;
4753 if (_ItemCreatedCallbacks.TryGetValue(dataBlock.CallbackID, out callback))
4754 {
4755 _ItemCreatedCallbacks.Remove(dataBlock.CallbackID);
4756  
4757 try { callback(true, item); }
4758 catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, Client, ex); }
4759 }
4760  
4761 // Look for an "item copied" callback
4762 ItemCopiedCallback copyCallback;
4763 if (_ItemCopiedCallbacks.TryGetValue(dataBlock.CallbackID, out copyCallback))
4764 {
4765 _ItemCopiedCallbacks.Remove(dataBlock.CallbackID);
4766  
4767 try { copyCallback(item); }
4768 catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, Client, ex); }
4769 }
4770 }
4771 }
4772 }
4773  
4774 /// <summary>Process an incoming packet and raise the appropriate events</summary>
4775 /// <param name="sender">The sender</param>
4776 /// <param name="e">The EventArgs object containing the packet data</param>
4777 protected void FetchInventoryReplyHandler(object sender, PacketReceivedEventArgs e)
4778 {
4779 Packet packet = e.Packet;
4780  
4781 FetchInventoryReplyPacket reply = packet as FetchInventoryReplyPacket;
4782  
4783 foreach (FetchInventoryReplyPacket.InventoryDataBlock dataBlock in reply.InventoryData)
4784 {
4785 if (dataBlock.InvType == (sbyte)InventoryType.Folder)
4786 {
4787 Logger.Log("Received FetchInventoryReply for an inventory folder, this should not happen!",
4788 Helpers.LogLevel.Error, Client);
4789 continue;
4790 }
4791  
4792 InventoryItem item = CreateInventoryItem((InventoryType)dataBlock.InvType, dataBlock.ItemID);
4793 item.AssetType = (AssetType)dataBlock.Type;
4794 item.AssetUUID = dataBlock.AssetID;
4795 item.CreationDate = Utils.UnixTimeToDateTime(dataBlock.CreationDate);
4796 item.CreatorID = dataBlock.CreatorID;
4797 item.Description = Utils.BytesToString(dataBlock.Description);
4798 item.Flags = dataBlock.Flags;
4799 item.GroupID = dataBlock.GroupID;
4800 item.GroupOwned = dataBlock.GroupOwned;
4801 item.InventoryType = (InventoryType)dataBlock.InvType;
4802 item.Name = Utils.BytesToString(dataBlock.Name);
4803 item.OwnerID = dataBlock.OwnerID;
4804 item.ParentUUID = dataBlock.FolderID;
4805 item.Permissions = new Permissions(
4806 dataBlock.BaseMask,
4807 dataBlock.EveryoneMask,
4808 dataBlock.GroupMask,
4809 dataBlock.NextOwnerMask,
4810 dataBlock.OwnerMask);
4811 item.SalePrice = dataBlock.SalePrice;
4812 item.SaleType = (SaleType)dataBlock.SaleType;
4813 item.UUID = dataBlock.ItemID;
4814  
4815 _Store[item.UUID] = item;
4816  
4817 // Fire the callback for an item being fetched
4818 OnItemReceived(new ItemReceivedEventArgs(item));
4819 }
4820 }
4821  
4822 /// <summary>Process an incoming packet and raise the appropriate events</summary>
4823 /// <param name="sender">The sender</param>
4824 /// <param name="e">The EventArgs object containing the packet data</param>
4825 protected void ReplyTaskInventoryHandler(object sender, PacketReceivedEventArgs e)
4826 {
4827 if (m_TaskInventoryReply != null)
4828 {
4829 Packet packet = e.Packet;
4830  
4831 ReplyTaskInventoryPacket reply = (ReplyTaskInventoryPacket)packet;
4832  
4833 OnTaskInventoryReply(new TaskInventoryReplyEventArgs(reply.InventoryData.TaskID, reply.InventoryData.Serial,
4834 Utils.BytesToString(reply.InventoryData.Filename)));
4835 }
4836 }
4837  
4838 protected void ScriptRunningReplyMessageHandler(string capsKey, Interfaces.IMessage message, Simulator simulator)
4839 {
4840 if (m_ScriptRunningReply != null)
4841 {
4842 ScriptRunningReplyMessage msg = (ScriptRunningReplyMessage)message;
4843 OnScriptRunningReply(new ScriptRunningReplyEventArgs(msg.ObjectID, msg.ItemID, msg.Mono, msg.Running));
4844 }
4845 }
4846  
4847 #endregion Packet Handlers
4848 }
4849  
4850 #region EventArgs
4851  
4852 public class InventoryObjectOfferedEventArgs : EventArgs
4853 {
4854 private readonly InstantMessage m_Offer;
4855 private readonly AssetType m_AssetType;
4856 private readonly UUID m_ObjectID;
4857 private readonly bool m_FromTask;
4858  
4859 /// <summary>Set to true to accept offer, false to decline it</summary>
4860 public bool Accept { get; set; }
4861 /// <summary>The folder to accept the inventory into, if null default folder for <see cref="AssetType"/> will be used</summary>
4862 public UUID FolderID { get; set; }
4863  
4864 public InstantMessage Offer { get { return m_Offer; } }
4865 public AssetType AssetType { get { return m_AssetType; } }
4866 public UUID ObjectID { get { return m_ObjectID; } }
4867 public bool FromTask { get { return m_FromTask; } }
4868  
4869 public InventoryObjectOfferedEventArgs(InstantMessage offerDetails, AssetType type, UUID objectID, bool fromTask, UUID folderID)
4870 {
4871 this.Accept = false;
4872 this.FolderID = folderID;
4873 this.m_Offer = offerDetails;
4874 this.m_AssetType = type;
4875 this.m_ObjectID = objectID;
4876 this.m_FromTask = fromTask;
4877 }
4878 }
4879  
4880 public class FolderUpdatedEventArgs : EventArgs
4881 {
4882 private readonly UUID m_FolderID;
4883 public UUID FolderID { get { return m_FolderID; } }
4884 private readonly bool m_Success;
4885 public bool Success { get { return m_Success; } }
4886  
4887 public FolderUpdatedEventArgs(UUID folderID, bool success)
4888 {
4889 this.m_FolderID = folderID;
4890 this.m_Success = success;
4891 }
4892 }
4893  
4894 public class ItemReceivedEventArgs : EventArgs
4895 {
4896 private readonly InventoryItem m_Item;
4897  
4898 public InventoryItem Item { get { return m_Item; } }
4899  
4900 public ItemReceivedEventArgs(InventoryItem item)
4901 {
4902 this.m_Item = item;
4903 }
4904 }
4905  
4906 public class FindObjectByPathReplyEventArgs : EventArgs
4907 {
4908 private readonly String m_Path;
4909 private readonly UUID m_InventoryObjectID;
4910  
4911 public String Path { get { return m_Path; } }
4912 public UUID InventoryObjectID { get { return m_InventoryObjectID; } }
4913  
4914 public FindObjectByPathReplyEventArgs(string path, UUID inventoryObjectID)
4915 {
4916 this.m_Path = path;
4917 this.m_InventoryObjectID = inventoryObjectID;
4918 }
4919 }
4920  
4921 /// <summary>
4922 /// Callback when an inventory object is accepted and received from a
4923 /// task inventory. This is the callback in which you actually get
4924 /// the ItemID, as in ObjectOfferedCallback it is null when received
4925 /// from a task.
4926 /// </summary>
4927 public class TaskItemReceivedEventArgs : EventArgs
4928 {
4929 private readonly UUID m_ItemID;
4930 private readonly UUID m_FolderID;
4931 private readonly UUID m_CreatorID;
4932 private readonly UUID m_AssetID;
4933 private readonly InventoryType m_Type;
4934  
4935 public UUID ItemID { get { return m_ItemID; } }
4936 public UUID FolderID { get { return m_FolderID; } }
4937 public UUID CreatorID { get { return m_CreatorID; } }
4938 public UUID AssetID { get { return m_AssetID; } }
4939 public InventoryType Type { get { return m_Type; } }
4940  
4941 public TaskItemReceivedEventArgs(UUID itemID, UUID folderID, UUID creatorID, UUID assetID, InventoryType type)
4942 {
4943 this.m_ItemID = itemID;
4944 this.m_FolderID = folderID;
4945 this.m_CreatorID = creatorID;
4946 this.m_AssetID = assetID;
4947 this.m_Type = type;
4948 }
4949 }
4950  
4951 public class TaskInventoryReplyEventArgs : EventArgs
4952 {
4953 private readonly UUID m_ItemID;
4954 private readonly Int16 m_Serial;
4955 private readonly String m_AssetFilename;
4956  
4957 public UUID ItemID { get { return m_ItemID; } }
4958 public Int16 Serial { get { return m_Serial; } }
4959 public String AssetFilename { get { return m_AssetFilename; } }
4960  
4961 public TaskInventoryReplyEventArgs(UUID itemID, short serial, string assetFilename)
4962 {
4963 this.m_ItemID = itemID;
4964 this.m_Serial = serial;
4965 this.m_AssetFilename = assetFilename;
4966 }
4967 }
4968  
4969 public class SaveAssetToInventoryEventArgs : EventArgs
4970 {
4971 private readonly UUID m_ItemID;
4972 private readonly UUID m_NewAssetID;
4973  
4974 public UUID ItemID { get { return m_ItemID; } }
4975 public UUID NewAssetID { get { return m_NewAssetID; } }
4976  
4977 public SaveAssetToInventoryEventArgs(UUID itemID, UUID newAssetID)
4978 {
4979 this.m_ItemID = itemID;
4980 this.m_NewAssetID = newAssetID;
4981 }
4982 }
4983  
4984 public class ScriptRunningReplyEventArgs : EventArgs
4985 {
4986 private readonly UUID m_ObjectID;
4987 private readonly UUID m_ScriptID;
4988 private readonly bool m_IsMono;
4989 private readonly bool m_IsRunning;
4990  
4991 public UUID ObjectID { get { return m_ObjectID; } }
4992 public UUID ScriptID { get { return m_ScriptID; } }
4993 public bool IsMono { get { return m_IsMono; } }
4994 public bool IsRunning { get { return m_IsRunning; } }
4995  
4996 public ScriptRunningReplyEventArgs(UUID objectID, UUID sctriptID, bool isMono, bool isRunning)
4997 {
4998 this.m_ObjectID = objectID;
4999 this.m_ScriptID = sctriptID;
5000 this.m_IsMono = isMono;
5001 this.m_IsRunning = isRunning;
5002 }
5003 }
5004 #endregion
5005 }