aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs69
-rw-r--r--OpenSim/Region/Framework/Scenes/ScenePresence.cs16
-rw-r--r--OpenSim/Services/HypergridService/HGSuitcaseInventoryService.cs368
3 files changed, 430 insertions, 23 deletions
diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs
index 4cdf303..ec260b4 100644
--- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs
+++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs
@@ -71,7 +71,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
71 if (name == Name) 71 if (name == Name)
72 { 72 {
73 InitialiseCommon(source); 73 InitialiseCommon(source);
74 IConfig transferConfig = source.Configs["HGEntityTransfer"]; 74 IConfig transferConfig = source.Configs["HGEntityTransferModule"];
75 if (transferConfig != null) 75 if (transferConfig != null)
76 m_RestrictInventoryAccessAbroad = transferConfig.GetBoolean("RestrictInventoryAccessAbroad", false); 76 m_RestrictInventoryAccessAbroad = transferConfig.GetBoolean("RestrictInventoryAccessAbroad", false);
77 77
@@ -94,6 +94,31 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
94 client.OnTeleportHomeRequest += TeleportHome; 94 client.OnTeleportHomeRequest += TeleportHome;
95 client.OnTeleportLandmarkRequest += RequestTeleportLandmark; 95 client.OnTeleportLandmarkRequest += RequestTeleportLandmark;
96 client.OnConnectionClosed += new Action<IClientAPI>(OnConnectionClosed); 96 client.OnConnectionClosed += new Action<IClientAPI>(OnConnectionClosed);
97 client.OnCompleteMovementToRegion += new Action<IClientAPI, bool>(OnCompleteMovementToRegion);
98 }
99
100 protected void OnCompleteMovementToRegion(IClientAPI client, bool arg2)
101 {
102 // HACK HACK -- just seeing how the viewer responds
103 // Let's send the Suitcase or the real root folder folder for incoming HG agents
104 // Visiting agents get their suitcase contents; incoming local users get their real root folder's content
105 m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: OnCompleteMovementToRegion of user {0}", client.AgentId);
106 object sp = null;
107 if (client.Scene.TryGetScenePresence(client.AgentId, out sp))
108 {
109 if (sp is ScenePresence)
110 {
111 AgentCircuitData aCircuit = ((ScenePresence)sp).Scene.AuthenticateHandler.GetAgentCircuitData(client.AgentId);
112 if ((aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaHGLogin) != 0)
113 {
114 m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: ViaHGLogin");
115 if (m_RestrictInventoryAccessAbroad)
116 {
117 RestoreRootFolderContents(client);
118 }
119 }
120 }
121 }
97 } 122 }
98 123
99 124
@@ -105,6 +130,13 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
105 { 130 {
106 m_GatekeeperConnector = new GatekeeperServiceConnector(scene.AssetService); 131 m_GatekeeperConnector = new GatekeeperServiceConnector(scene.AssetService);
107 m_Initialized = true; 132 m_Initialized = true;
133
134 scene.AddCommand(
135 "HG", this, "send inventory",
136 "send inventory",
137 "Don't use this",
138 HandleSendInventory);
139
108 } 140 }
109 141
110 } 142 }
@@ -369,7 +401,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
369 InventoryFolderBase root = m_Scenes[0].InventoryService.GetRootFolder(client.AgentId); 401 InventoryFolderBase root = m_Scenes[0].InventoryService.GetRootFolder(client.AgentId);
370 if (root != null) 402 if (root != null)
371 { 403 {
372 m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Removing root inventory"); 404 m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Removing root inventory for user {0}", client.AgentId);
373 InventoryCollection content = m_Scenes[0].InventoryService.GetFolderContent(client.AgentId, root.ID); 405 InventoryCollection content = m_Scenes[0].InventoryService.GetFolderContent(client.AgentId, root.ID);
374 UUID[] ids = new UUID[content.Folders.Count]; 406 UUID[] ids = new UUID[content.Folders.Count];
375 int i = 0; 407 int i = 0;
@@ -388,12 +420,26 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
388 420
389 private void RestoreRootFolderContents(IClientAPI client) 421 private void RestoreRootFolderContents(IClientAPI client)
390 { 422 {
391 // Restore the user's inventory, because we removed it earlier on 423 if (client is IClientCore)
392 InventoryFolderBase root = m_Scenes[0].InventoryService.GetRootFolder(client.AgentId);
393 if (root != null)
394 { 424 {
395 m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Restoring root inventory"); 425 IClientCore core = (IClientCore)client;
396 client.SendBulkUpdateInventory(root); 426 IClientInventory inv;
427
428 if (core.TryGet<IClientInventory>(out inv))
429 {
430 InventoryFolderBase root = m_Scenes[0].InventoryService.GetRootFolder(client.AgentId);
431 client.SendBulkUpdateInventory(root);
432 //if (root != null)
433 //{
434 // m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Restoring root inventory for user {0}", client.AgentId);
435 // InventoryCollection content = m_Scenes[0].InventoryService.GetFolderContent(client.AgentId, root.ID);
436 // m_log.DebugFormat("[XXX]: Folder name {0}, id {1}, parent {2}", root.Name, root.ID, root.ParentID);
437 // foreach (InventoryItemBase i in content.Items)
438 // m_log.DebugFormat("[XXX]: Name={0}, folderID={1}", i.Name, i.Folder);
439
440 // inv.SendBulkUpdateInventory(content.Folders.ToArray(), content.Items.ToArray());
441 //}
442 }
397 } 443 }
398 } 444 }
399 445
@@ -413,5 +459,14 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
413 region.InternalEndPoint = new System.Net.IPEndPoint(System.Net.IPAddress.Parse("0.0.0.0"), (int)0); 459 region.InternalEndPoint = new System.Net.IPEndPoint(System.Net.IPAddress.Parse("0.0.0.0"), (int)0);
414 return region; 460 return region;
415 } 461 }
462
463 protected void HandleSendInventory(string module, string[] cmd)
464 {
465 m_Scenes[0].ForEachClient(delegate(IClientAPI client)
466 {
467 RestoreRootFolderContents(client);
468 });
469 }
470
416 } 471 }
417} 472}
diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs
index 547f66f..19dab32 100644
--- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs
+++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs
@@ -1163,22 +1163,6 @@ namespace OpenSim.Region.Framework.Scenes
1163 friendsModule.SendFriendsOnlineIfNeeded(ControllingClient); 1163 friendsModule.SendFriendsOnlineIfNeeded(ControllingClient);
1164 } 1164 }
1165 1165
1166 // HACK HACK -- just seeing how the viewer responds
1167 // Let's send the Suitcase or the real root folder folder for incoming HG agents
1168 // Visiting agents get their suitcase contents; incoming local users get their real root folder's content
1169 AgentCircuitData aCircuit = m_scene.AuthenticateHandler.GetAgentCircuitData(UUID);
1170 if ((aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaHGLogin) != 0)
1171 {
1172 // HACK FOR NOW. JUST TESTING, SO KEEPING EVERYONE ELSE OUT OF THESE TESTS
1173 IConfig config = m_scene.Config.Configs["HGEntityTransferModule"];
1174 if (config != null && config.GetBoolean("RestrictInventoryAccessAbroad", false))
1175 {
1176 m_log.DebugFormat("[SCENE]: Sending root folder to viewer...");
1177 InventoryFolderBase root = m_scene.InventoryService.GetRootFolder(client.AgentId);
1178 //InventoryCollection rootContents = InventoryService.GetFolderContent(client.AgentId, root.ID);
1179 client.SendBulkUpdateInventory(root);
1180 }
1181 }
1182 1166
1183// m_log.DebugFormat( 1167// m_log.DebugFormat(
1184// "[SCENE PRESENCE]: Completing movement of {0} into region {1} took {2}ms", 1168// "[SCENE PRESENCE]: Completing movement of {0} into region {1} took {2}ms",
diff --git a/OpenSim/Services/HypergridService/HGSuitcaseInventoryService.cs b/OpenSim/Services/HypergridService/HGSuitcaseInventoryService.cs
new file mode 100644
index 0000000..a999886
--- /dev/null
+++ b/OpenSim/Services/HypergridService/HGSuitcaseInventoryService.cs
@@ -0,0 +1,368 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using OpenMetaverse;
31using log4net;
32using Nini.Config;
33using System.Reflection;
34using OpenSim.Services.Base;
35using OpenSim.Services.Interfaces;
36using OpenSim.Services.InventoryService;
37using OpenSim.Data;
38using OpenSim.Framework;
39using OpenSim.Server.Base;
40
41namespace OpenSim.Services.HypergridService
42{
43 /// <summary>
44 /// Hypergrid inventory service. It serves the IInventoryService interface,
45 /// but implements it in ways that are appropriate for inter-grid
46 /// inventory exchanges. Specifically, it does not performs deletions
47 /// and it responds to GetRootFolder requests with the ID of the
48 /// Suitcase folder, not the actual "My Inventory" folder.
49 /// </summary>
50 public class HGSuitcaseInventoryService : XInventoryService, IInventoryService
51 {
52 private static readonly ILog m_log =
53 LogManager.GetLogger(
54 MethodBase.GetCurrentMethod().DeclaringType);
55
56 private string m_HomeURL;
57 private IUserAccountService m_UserAccountService;
58
59 private UserAccountCache m_Cache;
60
61 public HGSuitcaseInventoryService(IConfigSource config, string configName)
62 : base(config, configName)
63 {
64 m_log.DebugFormat("[HG SUITCASE INVENTORY SERVICE]: Starting with config name {0}", configName);
65 if (configName != string.Empty)
66 m_ConfigName = configName;
67
68 if (m_Database == null)
69 m_log.WarnFormat("[XXX]: m_Database is null!");
70
71 //
72 // Try reading the [InventoryService] section, if it exists
73 //
74 IConfig invConfig = config.Configs[m_ConfigName];
75 if (invConfig != null)
76 {
77 // realm = authConfig.GetString("Realm", realm);
78 string userAccountsDll = invConfig.GetString("UserAccountsService", string.Empty);
79 if (userAccountsDll == string.Empty)
80 throw new Exception("Please specify UserAccountsService in HGInventoryService configuration");
81
82 Object[] args = new Object[] { config };
83 m_UserAccountService = ServerUtils.LoadPlugin<IUserAccountService>(userAccountsDll, args);
84 if (m_UserAccountService == null)
85 throw new Exception(String.Format("Unable to create UserAccountService from {0}", userAccountsDll));
86
87 // legacy configuration [obsolete]
88 m_HomeURL = invConfig.GetString("ProfileServerURI", string.Empty);
89 // Preferred
90 m_HomeURL = invConfig.GetString("HomeURI", m_HomeURL);
91
92 m_Cache = UserAccountCache.CreateUserAccountCache(m_UserAccountService);
93 }
94
95 m_log.Debug("[HG SUITCASE INVENTORY SERVICE]: Starting...");
96 }
97
98 public override bool CreateUserInventory(UUID principalID)
99 {
100 // NOGO
101 return false;
102 }
103
104
105 public override List<InventoryFolderBase> GetInventorySkeleton(UUID principalID)
106 {
107 // NOGO for this inventory service
108 return new List<InventoryFolderBase>();
109 }
110
111 public override InventoryFolderBase GetRootFolder(UUID principalID)
112 {
113 m_log.DebugFormat("[HG SUITCASE INVENTORY SERVICE]: GetRootFolder for {0}", principalID);
114 if (m_Database == null)
115 m_log.ErrorFormat("[XXX]: m_Database is NULL!");
116
117 // Let's find out the local root folder
118 XInventoryFolder root = GetRootXFolder(principalID); ;
119 if (root == null)
120 {
121 m_log.WarnFormat("[HG SUITCASE INVENTORY SERVICE]: Unable to retrieve local root folder for user {0}", principalID);
122 }
123
124 // Warp! Root folder for travelers is the suitcase folder
125 XInventoryFolder suitcase = GetSuitcaseXFolder(principalID);
126
127 if (suitcase == null)
128 {
129 m_log.DebugFormat("[HG SUITCASE INVENTORY SERVICE]: User {0} does not have a Suitcase folder. Creating it...", principalID);
130 // make one, and let's add it to the user's inventory as a direct child of the root folder
131 suitcase = CreateFolder(principalID, root.folderID, 100, "My Suitcase");
132 if (suitcase == null)
133 m_log.ErrorFormat("[HG SUITCASE INVENTORY SERVICE]: Unable to create suitcase folder");
134
135 m_Database.StoreFolder(suitcase);
136 }
137
138 // Now let's change the folder ID to match that of the real root folder
139 SetAsRootFolder(suitcase, root.folderID);
140
141 return ConvertToOpenSim(suitcase);
142 }
143
144 public override InventoryFolderBase GetFolderForType(UUID principalID, AssetType type)
145 {
146 //m_log.DebugFormat("[HG INVENTORY SERVICE]: GetFolderForType for {0} {0}", principalID, type);
147 return GetRootFolder(principalID);
148 }
149
150 //
151 // Use the inherited methods
152 //
153 public override InventoryCollection GetFolderContent(UUID principalID, UUID folderID)
154 {
155 InventoryCollection coll = null;
156 XInventoryFolder root = GetRootXFolder(principalID);
157 if (folderID == root.folderID) // someone's asking for the root folder, we'll give them the suitcase
158 {
159 XInventoryFolder suitcase = GetSuitcaseXFolder(principalID);
160 if (suitcase != null)
161 {
162 coll = base.GetFolderContent(principalID, suitcase.folderID);
163 foreach (InventoryFolderBase f in coll.Folders)
164 f.ParentID = root.folderID;
165 foreach (InventoryItemBase i in coll.Items)
166 i.Folder = root.folderID;
167 m_log.DebugFormat("[HG SUITCASE INVENTORY SERVICE]: GetFolderContent for root folder returned content for suitcase folder");
168 }
169 }
170 else
171 {
172 coll = base.GetFolderContent(principalID, folderID);
173 m_log.DebugFormat("[HG SUITCASE INVENTORY SERVICE]: GetFolderContent for non-root folder {0}", folderID);
174 }
175 if (coll == null)
176 {
177 m_log.WarnFormat("[HG SUITCASE INVENTORY SERVICE]: Something wrong with user {0}'s suitcase folder", principalID);
178 coll = new InventoryCollection();
179 }
180 return coll;
181 }
182
183 //public List<InventoryItemBase> GetFolderItems(UUID principalID, UUID folderID)
184 //{
185 //}
186
187 //public override bool AddFolder(InventoryFolderBase folder)
188 //{
189 // // Check if it's under the Suitcase folder
190 // List<InventoryFolderBase> skel = base.GetInventorySkeleton(folder.Owner);
191 // InventoryFolderBase suitcase = GetRootFolder(folder.Owner);
192 // List<InventoryFolderBase> suitDescendents = GetDescendents(skel, suitcase.ID);
193
194 // foreach (InventoryFolderBase f in suitDescendents)
195 // if (folder.ParentID == f.ID)
196 // {
197 // XInventoryFolder xFolder = ConvertFromOpenSim(folder);
198 // return m_Database.StoreFolder(xFolder);
199 // }
200 // return false;
201 //}
202
203 private List<InventoryFolderBase> GetDescendents(List<InventoryFolderBase> lst, UUID root)
204 {
205 List<InventoryFolderBase> direct = lst.FindAll(delegate(InventoryFolderBase f) { return f.ParentID == root; });
206 if (direct == null)
207 return new List<InventoryFolderBase>();
208
209 List<InventoryFolderBase> indirect = new List<InventoryFolderBase>();
210 foreach (InventoryFolderBase f in direct)
211 indirect.AddRange(GetDescendents(lst, f.ID));
212
213 direct.AddRange(indirect);
214 return direct;
215 }
216
217 // Use inherited method
218 //public bool UpdateFolder(InventoryFolderBase folder)
219 //{
220 //}
221
222 //public override bool MoveFolder(InventoryFolderBase folder)
223 //{
224 // XInventoryFolder[] x = m_Database.GetFolders(
225 // new string[] { "folderID" },
226 // new string[] { folder.ID.ToString() });
227
228 // if (x.Length == 0)
229 // return false;
230
231 // // Check if it's under the Suitcase folder
232 // List<InventoryFolderBase> skel = base.GetInventorySkeleton(folder.Owner);
233 // InventoryFolderBase suitcase = GetRootFolder(folder.Owner);
234 // List<InventoryFolderBase> suitDescendents = GetDescendents(skel, suitcase.ID);
235
236 // foreach (InventoryFolderBase f in suitDescendents)
237 // if (folder.ParentID == f.ID)
238 // {
239 // x[0].parentFolderID = folder.ParentID;
240 // return m_Database.StoreFolder(x[0]);
241 // }
242
243 // return false;
244 //}
245
246 public override bool DeleteFolders(UUID principalID, List<UUID> folderIDs)
247 {
248 // NOGO
249 return false;
250 }
251
252 public override bool PurgeFolder(InventoryFolderBase folder)
253 {
254 // NOGO
255 return false;
256 }
257
258 // Unfortunately we need to use the inherited method because of how DeRez works.
259 // The viewer sends the folderID hard-wired in the derez message
260 //public override bool AddItem(InventoryItemBase item)
261 //{
262 // // Check if it's under the Suitcase folder
263 // List<InventoryFolderBase> skel = base.GetInventorySkeleton(item.Owner);
264 // InventoryFolderBase suitcase = GetRootFolder(item.Owner);
265 // List<InventoryFolderBase> suitDescendents = GetDescendents(skel, suitcase.ID);
266
267 // foreach (InventoryFolderBase f in suitDescendents)
268 // if (item.Folder == f.ID)
269 // return m_Database.StoreItem(ConvertFromOpenSim(item));
270
271 // return false;
272 //}
273
274 //public override bool UpdateItem(InventoryItemBase item)
275 //{
276 // // Check if it's under the Suitcase folder
277 // List<InventoryFolderBase> skel = base.GetInventorySkeleton(item.Owner);
278 // InventoryFolderBase suitcase = GetRootFolder(item.Owner);
279 // List<InventoryFolderBase> suitDescendents = GetDescendents(skel, suitcase.ID);
280
281 // foreach (InventoryFolderBase f in suitDescendents)
282 // if (item.Folder == f.ID)
283 // return m_Database.StoreItem(ConvertFromOpenSim(item));
284
285 // return false;
286 //}
287
288 //public override bool MoveItems(UUID principalID, List<InventoryItemBase> items)
289 //{
290 // // Principal is b0rked. *sigh*
291 // //
292 // // Let's assume they all have the same principal
293 // // Check if it's under the Suitcase folder
294 // List<InventoryFolderBase> skel = base.GetInventorySkeleton(items[0].Owner);
295 // InventoryFolderBase suitcase = GetRootFolder(items[0].Owner);
296 // List<InventoryFolderBase> suitDescendents = GetDescendents(skel, suitcase.ID);
297
298 // foreach (InventoryItemBase i in items)
299 // {
300 // foreach (InventoryFolderBase f in suitDescendents)
301 // if (i.Folder == f.ID)
302 // m_Database.MoveItem(i.ID.ToString(), i.Folder.ToString());
303 // }
304
305 // return true;
306 //}
307
308 // Let these pass. Use inherited methods.
309 //public bool DeleteItems(UUID principalID, List<UUID> itemIDs)
310 //{
311 //}
312
313 //public override InventoryItemBase GetItem(InventoryItemBase item)
314 //{
315 // InventoryItemBase it = base.GetItem(item);
316 // if (it != null)
317 // {
318 // UserAccount user = m_Cache.GetUser(it.CreatorId);
319
320 // // Adjust the creator data
321 // if (user != null && it != null && (it.CreatorData == null || it.CreatorData == string.Empty))
322 // it.CreatorData = m_HomeURL + ";" + user.FirstName + " " + user.LastName;
323 // }
324 // return it;
325 //}
326
327 //public InventoryFolderBase GetFolder(InventoryFolderBase folder)
328 //{
329 //}
330
331 //public List<InventoryItemBase> GetActiveGestures(UUID principalID)
332 //{
333 //}
334
335 //public int GetAssetPermissions(UUID principalID, UUID assetID)
336 //{
337 //}
338
339 private XInventoryFolder GetRootXFolder(UUID principalID)
340 {
341 XInventoryFolder[] folders = m_Database.GetFolders(
342 new string[] { "agentID", "folderName", "type" },
343 new string[] { principalID.ToString(), "My Inventory", ((int)AssetType.RootFolder).ToString() });
344
345 if (folders != null && folders.Length > 0)
346 return folders[0];
347 return null;
348 }
349
350 private XInventoryFolder GetSuitcaseXFolder(UUID principalID)
351 {
352 // Warp! Root folder for travelers
353 XInventoryFolder[] folders = m_Database.GetFolders(
354 new string[] { "agentID", "type" },
355 new string[] { principalID.ToString(), "100" }); // This is a special folder type...
356
357 if (folders != null && folders.Length > 0)
358 return folders[0];
359 return null;
360 }
361
362 private void SetAsRootFolder(XInventoryFolder suitcase, UUID rootID)
363 {
364 suitcase.folderID = rootID;
365 suitcase.parentFolderID = UUID.Zero;
366 }
367 }
368}