diff options
Diffstat (limited to 'OpenSim/Data/SQLiteNG/SQLiteInventoryStore.cs')
-rw-r--r-- | OpenSim/Data/SQLiteNG/SQLiteInventoryStore.cs | 909 |
1 files changed, 909 insertions, 0 deletions
diff --git a/OpenSim/Data/SQLiteNG/SQLiteInventoryStore.cs b/OpenSim/Data/SQLiteNG/SQLiteInventoryStore.cs new file mode 100644 index 0000000..ece2495 --- /dev/null +++ b/OpenSim/Data/SQLiteNG/SQLiteInventoryStore.cs | |||
@@ -0,0 +1,909 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSimulator Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.Data; | ||
31 | using System.Reflection; | ||
32 | using log4net; | ||
33 | using Mono.Data.Sqlite; | ||
34 | using OpenMetaverse; | ||
35 | using OpenSim.Framework; | ||
36 | |||
37 | namespace OpenSim.Data.SQLite | ||
38 | { | ||
39 | /// <summary> | ||
40 | /// An Inventory Interface to the SQLite database | ||
41 | /// </summary> | ||
42 | public class SQLiteInventoryStore : SQLiteUtil, IInventoryDataPlugin | ||
43 | { | ||
44 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
45 | |||
46 | private const string invItemsSelect = "select * from inventoryitems"; | ||
47 | private const string invFoldersSelect = "select * from inventoryfolders"; | ||
48 | |||
49 | private static SqliteConnection conn; | ||
50 | private static DataSet ds; | ||
51 | private static SqliteDataAdapter invItemsDa; | ||
52 | private static SqliteDataAdapter invFoldersDa; | ||
53 | |||
54 | private static bool m_Initialized = false; | ||
55 | |||
56 | public void Initialise() | ||
57 | { | ||
58 | m_log.Info("[SQLiteInventoryData]: " + Name + " cannot be default-initialized!"); | ||
59 | throw new PluginNotInitialisedException(Name); | ||
60 | } | ||
61 | |||
62 | /// <summary> | ||
63 | /// <list type="bullet"> | ||
64 | /// <item>Initialises Inventory interface</item> | ||
65 | /// <item>Loads and initialises a new SQLite connection and maintains it.</item> | ||
66 | /// <item>use default URI if connect string string is empty.</item> | ||
67 | /// </list> | ||
68 | /// </summary> | ||
69 | /// <param name="dbconnect">connect string</param> | ||
70 | public void Initialise(string dbconnect) | ||
71 | { | ||
72 | if (!m_Initialized) | ||
73 | { | ||
74 | m_Initialized = true; | ||
75 | |||
76 | if (dbconnect == string.Empty) | ||
77 | { | ||
78 | dbconnect = "URI=file:inventoryStore.db,version=3"; | ||
79 | } | ||
80 | m_log.Info("[INVENTORY DB]: Sqlite - connecting: " + dbconnect); | ||
81 | conn = new SqliteConnection(dbconnect); | ||
82 | |||
83 | conn.Open(); | ||
84 | |||
85 | Assembly assem = GetType().Assembly; | ||
86 | Migration m = new Migration(conn, assem, "InventoryStore"); | ||
87 | m.Update(); | ||
88 | |||
89 | SqliteCommand itemsSelectCmd = new SqliteCommand(invItemsSelect, conn); | ||
90 | invItemsDa = new SqliteDataAdapter(itemsSelectCmd); | ||
91 | // SqliteCommandBuilder primCb = new SqliteCommandBuilder(primDa); | ||
92 | |||
93 | SqliteCommand foldersSelectCmd = new SqliteCommand(invFoldersSelect, conn); | ||
94 | invFoldersDa = new SqliteDataAdapter(foldersSelectCmd); | ||
95 | |||
96 | ds = new DataSet(); | ||
97 | |||
98 | ds.Tables.Add(createInventoryFoldersTable()); | ||
99 | invFoldersDa.Fill(ds.Tables["inventoryfolders"]); | ||
100 | setupFoldersCommands(invFoldersDa, conn); | ||
101 | CreateDataSetMapping(invFoldersDa, "inventoryfolders"); | ||
102 | m_log.Info("[INVENTORY DB]: Populated Inventory Folders Definitions"); | ||
103 | |||
104 | ds.Tables.Add(createInventoryItemsTable()); | ||
105 | invItemsDa.Fill(ds.Tables["inventoryitems"]); | ||
106 | setupItemsCommands(invItemsDa, conn); | ||
107 | CreateDataSetMapping(invItemsDa, "inventoryitems"); | ||
108 | m_log.Info("[INVENTORY DB]: Populated Inventory Items Definitions"); | ||
109 | |||
110 | ds.AcceptChanges(); | ||
111 | } | ||
112 | } | ||
113 | |||
114 | /// <summary> | ||
115 | /// Closes the inventory interface | ||
116 | /// </summary> | ||
117 | public void Dispose() | ||
118 | { | ||
119 | if (conn != null) | ||
120 | { | ||
121 | conn.Close(); | ||
122 | conn = null; | ||
123 | } | ||
124 | if (invItemsDa != null) | ||
125 | { | ||
126 | invItemsDa.Dispose(); | ||
127 | invItemsDa = null; | ||
128 | } | ||
129 | if (invFoldersDa != null) | ||
130 | { | ||
131 | invFoldersDa.Dispose(); | ||
132 | invFoldersDa = null; | ||
133 | } | ||
134 | if (ds != null) | ||
135 | { | ||
136 | ds.Dispose(); | ||
137 | ds = null; | ||
138 | } | ||
139 | } | ||
140 | |||
141 | /// <summary> | ||
142 | /// | ||
143 | /// </summary> | ||
144 | /// <param name="row"></param> | ||
145 | /// <returns></returns> | ||
146 | public InventoryItemBase buildItem(DataRow row) | ||
147 | { | ||
148 | InventoryItemBase item = new InventoryItemBase(); | ||
149 | item.ID = new UUID((string) row["UUID"]); | ||
150 | item.AssetID = new UUID((string) row["assetID"]); | ||
151 | item.AssetType = Convert.ToInt32(row["assetType"]); | ||
152 | item.InvType = Convert.ToInt32(row["invType"]); | ||
153 | item.Folder = new UUID((string) row["parentFolderID"]); | ||
154 | item.Owner = new UUID((string) row["avatarID"]); | ||
155 | item.CreatorId = (string)row["creatorsID"]; | ||
156 | item.Name = (string) row["inventoryName"]; | ||
157 | item.Description = (string) row["inventoryDescription"]; | ||
158 | |||
159 | item.NextPermissions = Convert.ToUInt32(row["inventoryNextPermissions"]); | ||
160 | item.CurrentPermissions = Convert.ToUInt32(row["inventoryCurrentPermissions"]); | ||
161 | item.BasePermissions = Convert.ToUInt32(row["inventoryBasePermissions"]); | ||
162 | item.EveryOnePermissions = Convert.ToUInt32(row["inventoryEveryOnePermissions"]); | ||
163 | item.GroupPermissions = Convert.ToUInt32(row["inventoryGroupPermissions"]); | ||
164 | |||
165 | // new fields | ||
166 | if (!Convert.IsDBNull(row["salePrice"])) | ||
167 | item.SalePrice = Convert.ToInt32(row["salePrice"]); | ||
168 | |||
169 | if (!Convert.IsDBNull(row["saleType"])) | ||
170 | item.SaleType = Convert.ToByte(row["saleType"]); | ||
171 | |||
172 | if (!Convert.IsDBNull(row["creationDate"])) | ||
173 | item.CreationDate = Convert.ToInt32(row["creationDate"]); | ||
174 | |||
175 | if (!Convert.IsDBNull(row["groupID"])) | ||
176 | item.GroupID = new UUID((string)row["groupID"]); | ||
177 | |||
178 | if (!Convert.IsDBNull(row["groupOwned"])) | ||
179 | item.GroupOwned = Convert.ToBoolean(row["groupOwned"]); | ||
180 | |||
181 | if (!Convert.IsDBNull(row["Flags"])) | ||
182 | item.Flags = Convert.ToUInt32(row["Flags"]); | ||
183 | |||
184 | return item; | ||
185 | } | ||
186 | |||
187 | /// <summary> | ||
188 | /// Fill a database row with item data | ||
189 | /// </summary> | ||
190 | /// <param name="row"></param> | ||
191 | /// <param name="item"></param> | ||
192 | private static void fillItemRow(DataRow row, InventoryItemBase item) | ||
193 | { | ||
194 | row["UUID"] = item.ID.ToString(); | ||
195 | row["assetID"] = item.AssetID.ToString(); | ||
196 | row["assetType"] = item.AssetType; | ||
197 | row["invType"] = item.InvType; | ||
198 | row["parentFolderID"] = item.Folder.ToString(); | ||
199 | row["avatarID"] = item.Owner.ToString(); | ||
200 | row["creatorsID"] = item.CreatorId.ToString(); | ||
201 | row["inventoryName"] = item.Name; | ||
202 | row["inventoryDescription"] = item.Description; | ||
203 | |||
204 | row["inventoryNextPermissions"] = item.NextPermissions; | ||
205 | row["inventoryCurrentPermissions"] = item.CurrentPermissions; | ||
206 | row["inventoryBasePermissions"] = item.BasePermissions; | ||
207 | row["inventoryEveryOnePermissions"] = item.EveryOnePermissions; | ||
208 | row["inventoryGroupPermissions"] = item.GroupPermissions; | ||
209 | |||
210 | // new fields | ||
211 | row["salePrice"] = item.SalePrice; | ||
212 | row["saleType"] = item.SaleType; | ||
213 | row["creationDate"] = item.CreationDate; | ||
214 | row["groupID"] = item.GroupID.ToString(); | ||
215 | row["groupOwned"] = item.GroupOwned; | ||
216 | row["flags"] = item.Flags; | ||
217 | } | ||
218 | |||
219 | /// <summary> | ||
220 | /// Add inventory folder | ||
221 | /// </summary> | ||
222 | /// <param name="folder">Folder base</param> | ||
223 | /// <param name="add">true=create folder. false=update existing folder</param> | ||
224 | /// <remarks>nasty</remarks> | ||
225 | private void addFolder(InventoryFolderBase folder, bool add) | ||
226 | { | ||
227 | lock (ds) | ||
228 | { | ||
229 | DataTable inventoryFolderTable = ds.Tables["inventoryfolders"]; | ||
230 | |||
231 | DataRow inventoryRow = inventoryFolderTable.Rows.Find(folder.ID.ToString()); | ||
232 | if (inventoryRow == null) | ||
233 | { | ||
234 | if (! add) | ||
235 | m_log.ErrorFormat("Interface Misuse: Attempting to Update non-existant inventory folder: {0}", folder.ID); | ||
236 | |||
237 | inventoryRow = inventoryFolderTable.NewRow(); | ||
238 | fillFolderRow(inventoryRow, folder); | ||
239 | inventoryFolderTable.Rows.Add(inventoryRow); | ||
240 | } | ||
241 | else | ||
242 | { | ||
243 | if (add) | ||
244 | m_log.ErrorFormat("Interface Misuse: Attempting to Add inventory folder that already exists: {0}", folder.ID); | ||
245 | |||
246 | fillFolderRow(inventoryRow, folder); | ||
247 | } | ||
248 | |||
249 | invFoldersDa.Update(ds, "inventoryfolders"); | ||
250 | } | ||
251 | } | ||
252 | |||
253 | /// <summary> | ||
254 | /// Move an inventory folder | ||
255 | /// </summary> | ||
256 | /// <param name="folder">folder base</param> | ||
257 | private void moveFolder(InventoryFolderBase folder) | ||
258 | { | ||
259 | lock (ds) | ||
260 | { | ||
261 | DataTable inventoryFolderTable = ds.Tables["inventoryfolders"]; | ||
262 | |||
263 | DataRow inventoryRow = inventoryFolderTable.Rows.Find(folder.ID.ToString()); | ||
264 | if (inventoryRow == null) | ||
265 | { | ||
266 | inventoryRow = inventoryFolderTable.NewRow(); | ||
267 | fillFolderRow(inventoryRow, folder); | ||
268 | inventoryFolderTable.Rows.Add(inventoryRow); | ||
269 | } | ||
270 | else | ||
271 | { | ||
272 | moveFolderRow(inventoryRow, folder); | ||
273 | } | ||
274 | |||
275 | invFoldersDa.Update(ds, "inventoryfolders"); | ||
276 | } | ||
277 | } | ||
278 | |||
279 | /// <summary> | ||
280 | /// add an item in inventory | ||
281 | /// </summary> | ||
282 | /// <param name="item">the item</param> | ||
283 | /// <param name="add">true=add item ; false=update existing item</param> | ||
284 | private void addItem(InventoryItemBase item, bool add) | ||
285 | { | ||
286 | lock (ds) | ||
287 | { | ||
288 | DataTable inventoryItemTable = ds.Tables["inventoryitems"]; | ||
289 | |||
290 | DataRow inventoryRow = inventoryItemTable.Rows.Find(item.ID.ToString()); | ||
291 | if (inventoryRow == null) | ||
292 | { | ||
293 | if (!add) | ||
294 | m_log.ErrorFormat("[INVENTORY DB]: Interface Misuse: Attempting to Update non-existant inventory item: {0}", item.ID); | ||
295 | |||
296 | inventoryRow = inventoryItemTable.NewRow(); | ||
297 | fillItemRow(inventoryRow, item); | ||
298 | inventoryItemTable.Rows.Add(inventoryRow); | ||
299 | } | ||
300 | else | ||
301 | { | ||
302 | if (add) | ||
303 | m_log.ErrorFormat("[INVENTORY DB]: Interface Misuse: Attempting to Add inventory item that already exists: {0}", item.ID); | ||
304 | |||
305 | fillItemRow(inventoryRow, item); | ||
306 | } | ||
307 | |||
308 | invItemsDa.Update(ds, "inventoryitems"); | ||
309 | |||
310 | DataTable inventoryFolderTable = ds.Tables["inventoryfolders"]; | ||
311 | |||
312 | inventoryRow = inventoryFolderTable.Rows.Find(item.Folder.ToString()); | ||
313 | if (inventoryRow != null) //MySQL doesn't throw an exception here, so sqlite shouldn't either. | ||
314 | inventoryRow["version"] = (int)inventoryRow["version"] + 1; | ||
315 | |||
316 | invFoldersDa.Update(ds, "inventoryfolders"); | ||
317 | } | ||
318 | } | ||
319 | |||
320 | /// <summary> | ||
321 | /// TODO : DataSet commit | ||
322 | /// </summary> | ||
323 | public void Shutdown() | ||
324 | { | ||
325 | // TODO: DataSet commit | ||
326 | } | ||
327 | |||
328 | /// <summary> | ||
329 | /// The name of this DB provider | ||
330 | /// </summary> | ||
331 | /// <returns>Name of DB provider</returns> | ||
332 | public string Name | ||
333 | { | ||
334 | get { return "SQLite Inventory Data Interface"; } | ||
335 | } | ||
336 | |||
337 | /// <summary> | ||
338 | /// Returns the version of this DB provider | ||
339 | /// </summary> | ||
340 | /// <returns>A string containing the DB provider version</returns> | ||
341 | public string Version | ||
342 | { | ||
343 | get | ||
344 | { | ||
345 | Module module = GetType().Module; | ||
346 | // string dllName = module.Assembly.ManifestModule.Name; | ||
347 | Version dllVersion = module.Assembly.GetName().Version; | ||
348 | |||
349 | |||
350 | return | ||
351 | string.Format("{0}.{1}.{2}.{3}", dllVersion.Major, dllVersion.Minor, dllVersion.Build, | ||
352 | dllVersion.Revision); | ||
353 | } | ||
354 | } | ||
355 | |||
356 | /// <summary> | ||
357 | /// Returns a list of inventory items contained within the specified folder | ||
358 | /// </summary> | ||
359 | /// <param name="folderID">The UUID of the target folder</param> | ||
360 | /// <returns>A List of InventoryItemBase items</returns> | ||
361 | public List<InventoryItemBase> getInventoryInFolder(UUID folderID) | ||
362 | { | ||
363 | lock (ds) | ||
364 | { | ||
365 | List<InventoryItemBase> retval = new List<InventoryItemBase>(); | ||
366 | DataTable inventoryItemTable = ds.Tables["inventoryitems"]; | ||
367 | string selectExp = "parentFolderID = '" + folderID + "'"; | ||
368 | DataRow[] rows = inventoryItemTable.Select(selectExp); | ||
369 | foreach (DataRow row in rows) | ||
370 | { | ||
371 | retval.Add(buildItem(row)); | ||
372 | } | ||
373 | |||
374 | return retval; | ||
375 | } | ||
376 | } | ||
377 | |||
378 | /// <summary> | ||
379 | /// Returns a list of the root folders within a users inventory | ||
380 | /// </summary> | ||
381 | /// <param name="user">The user whos inventory is to be searched</param> | ||
382 | /// <returns>A list of folder objects</returns> | ||
383 | public List<InventoryFolderBase> getUserRootFolders(UUID user) | ||
384 | { | ||
385 | return new List<InventoryFolderBase>(); | ||
386 | } | ||
387 | |||
388 | // see InventoryItemBase.getUserRootFolder | ||
389 | public InventoryFolderBase getUserRootFolder(UUID user) | ||
390 | { | ||
391 | lock (ds) | ||
392 | { | ||
393 | List<InventoryFolderBase> folders = new List<InventoryFolderBase>(); | ||
394 | DataTable inventoryFolderTable = ds.Tables["inventoryfolders"]; | ||
395 | string selectExp = "agentID = '" + user + "' AND parentID = '" + UUID.Zero + "'"; | ||
396 | DataRow[] rows = inventoryFolderTable.Select(selectExp); | ||
397 | foreach (DataRow row in rows) | ||
398 | { | ||
399 | folders.Add(buildFolder(row)); | ||
400 | } | ||
401 | |||
402 | // There should only ever be one root folder for a user. However, if there's more | ||
403 | // than one we'll simply use the first one rather than failing. It would be even | ||
404 | // nicer to print some message to this effect, but this feels like it's too low a | ||
405 | // to put such a message out, and it's too minor right now to spare the time to | ||
406 | // suitably refactor. | ||
407 | if (folders.Count > 0) | ||
408 | { | ||
409 | return folders[0]; | ||
410 | } | ||
411 | |||
412 | return null; | ||
413 | } | ||
414 | } | ||
415 | |||
416 | /// <summary> | ||
417 | /// Append a list of all the child folders of a parent folder | ||
418 | /// </summary> | ||
419 | /// <param name="folders">list where folders will be appended</param> | ||
420 | /// <param name="parentID">ID of parent</param> | ||
421 | protected void getInventoryFolders(ref List<InventoryFolderBase> folders, UUID parentID) | ||
422 | { | ||
423 | lock (ds) | ||
424 | { | ||
425 | DataTable inventoryFolderTable = ds.Tables["inventoryfolders"]; | ||
426 | string selectExp = "parentID = '" + parentID + "'"; | ||
427 | DataRow[] rows = inventoryFolderTable.Select(selectExp); | ||
428 | foreach (DataRow row in rows) | ||
429 | { | ||
430 | folders.Add(buildFolder(row)); | ||
431 | } | ||
432 | |||
433 | } | ||
434 | } | ||
435 | |||
436 | /// <summary> | ||
437 | /// Returns a list of inventory folders contained in the folder 'parentID' | ||
438 | /// </summary> | ||
439 | /// <param name="parentID">The folder to get subfolders for</param> | ||
440 | /// <returns>A list of inventory folders</returns> | ||
441 | public List<InventoryFolderBase> getInventoryFolders(UUID parentID) | ||
442 | { | ||
443 | List<InventoryFolderBase> folders = new List<InventoryFolderBase>(); | ||
444 | getInventoryFolders(ref folders, parentID); | ||
445 | return folders; | ||
446 | } | ||
447 | |||
448 | /// <summary> | ||
449 | /// See IInventoryDataPlugin | ||
450 | /// </summary> | ||
451 | /// <param name="parentID"></param> | ||
452 | /// <returns></returns> | ||
453 | public List<InventoryFolderBase> getFolderHierarchy(UUID parentID) | ||
454 | { | ||
455 | /* Note: There are subtle changes between this implementation of getFolderHierarchy and the previous one | ||
456 | * - We will only need to hit the database twice instead of n times. | ||
457 | * - We assume the database is well-formed - no stranded/dangling folders, all folders in heirarchy owned | ||
458 | * by the same person, each user only has 1 inventory heirarchy | ||
459 | * - The returned list is not ordered, instead of breadth-first ordered | ||
460 | There are basically 2 usage cases for getFolderHeirarchy: | ||
461 | 1) Getting the user's entire inventory heirarchy when they log in | ||
462 | 2) Finding a subfolder heirarchy to delete when emptying the trash. | ||
463 | This implementation will pull all inventory folders from the database, and then prune away any folder that | ||
464 | is not part of the requested sub-heirarchy. The theory is that it is cheaper to make 1 request from the | ||
465 | database than to make n requests. This pays off only if requested heirarchy is large. | ||
466 | By making this choice, we are making the worst case better at the cost of making the best case worse | ||
467 | - Francis | ||
468 | */ | ||
469 | |||
470 | List<InventoryFolderBase> folders = new List<InventoryFolderBase>(); | ||
471 | DataRow[] folderRows = null, parentRow; | ||
472 | InventoryFolderBase parentFolder = null; | ||
473 | lock (ds) | ||
474 | { | ||
475 | /* Fetch the parent folder from the database to determine the agent ID. | ||
476 | * Then fetch all inventory folders for that agent from the agent ID. | ||
477 | */ | ||
478 | DataTable inventoryFolderTable = ds.Tables["inventoryfolders"]; | ||
479 | string selectExp = "UUID = '" + parentID + "'"; | ||
480 | parentRow = inventoryFolderTable.Select(selectExp); // Assume at most 1 result | ||
481 | if (parentRow.GetLength(0) >= 1) // No result means parent folder does not exist | ||
482 | { | ||
483 | parentFolder = buildFolder(parentRow[0]); | ||
484 | UUID agentID = parentFolder.Owner; | ||
485 | selectExp = "agentID = '" + agentID + "'"; | ||
486 | folderRows = inventoryFolderTable.Select(selectExp); | ||
487 | } | ||
488 | |||
489 | if (folderRows != null && folderRows.GetLength(0) >= 1) // No result means parent folder does not exist | ||
490 | { // or has no children | ||
491 | /* if we're querying the root folder, just return an unordered list of all folders in the user's | ||
492 | * inventory | ||
493 | */ | ||
494 | if (parentFolder.ParentID == UUID.Zero) | ||
495 | { | ||
496 | foreach (DataRow row in folderRows) | ||
497 | { | ||
498 | InventoryFolderBase curFolder = buildFolder(row); | ||
499 | if (curFolder.ID != parentID) // Return all folders except the parent folder of heirarchy | ||
500 | folders.Add(buildFolder(row)); | ||
501 | } | ||
502 | } // If requesting root folder | ||
503 | /* else we are querying a non-root folder. We currently have a list of all of the user's folders, | ||
504 | * we must construct a list of all folders in the heirarchy below parentID. | ||
505 | * Our first step will be to construct a hash table of all folders, indexed by parent ID. | ||
506 | * Once we have constructed the hash table, we will do a breadth-first traversal on the tree using the | ||
507 | * hash table to find child folders. | ||
508 | */ | ||
509 | else | ||
510 | { // Querying a non-root folder | ||
511 | |||
512 | // Build a hash table of all user's inventory folders, indexed by each folder's parent ID | ||
513 | Dictionary<UUID, List<InventoryFolderBase>> hashtable = | ||
514 | new Dictionary<UUID, List<InventoryFolderBase>>(folderRows.GetLength(0)); | ||
515 | |||
516 | foreach (DataRow row in folderRows) | ||
517 | { | ||
518 | InventoryFolderBase curFolder = buildFolder(row); | ||
519 | if (curFolder.ParentID != UUID.Zero) // Discard root of tree - not needed | ||
520 | { | ||
521 | if (hashtable.ContainsKey(curFolder.ParentID)) | ||
522 | { | ||
523 | // Current folder already has a sibling - append to sibling list | ||
524 | hashtable[curFolder.ParentID].Add(curFolder); | ||
525 | } | ||
526 | else | ||
527 | { | ||
528 | List<InventoryFolderBase> siblingList = new List<InventoryFolderBase>(); | ||
529 | siblingList.Add(curFolder); | ||
530 | // Current folder has no known (yet) siblings | ||
531 | hashtable.Add(curFolder.ParentID, siblingList); | ||
532 | } | ||
533 | } | ||
534 | } // For all inventory folders | ||
535 | |||
536 | // Note: Could release the ds lock here - we don't access folderRows or the database anymore. | ||
537 | // This is somewhat of a moot point as the callers of this function usually lock db anyways. | ||
538 | |||
539 | if (hashtable.ContainsKey(parentID)) // if requested folder does have children | ||
540 | folders.AddRange(hashtable[parentID]); | ||
541 | |||
542 | // BreadthFirstSearch build inventory tree **Note: folders.Count is *not* static | ||
543 | for (int i = 0; i < folders.Count; i++) | ||
544 | if (hashtable.ContainsKey(folders[i].ID)) | ||
545 | folders.AddRange(hashtable[folders[i].ID]); | ||
546 | |||
547 | } // if requesting a subfolder heirarchy | ||
548 | } // if folder parentID exists and has children | ||
549 | } // lock ds | ||
550 | return folders; | ||
551 | } | ||
552 | |||
553 | /// <summary> | ||
554 | /// Returns an inventory item by its UUID | ||
555 | /// </summary> | ||
556 | /// <param name="item">The UUID of the item to be returned</param> | ||
557 | /// <returns>A class containing item information</returns> | ||
558 | public InventoryItemBase getInventoryItem(UUID item) | ||
559 | { | ||
560 | lock (ds) | ||
561 | { | ||
562 | DataRow row = ds.Tables["inventoryitems"].Rows.Find(item.ToString()); | ||
563 | if (row != null) | ||
564 | { | ||
565 | return buildItem(row); | ||
566 | } | ||
567 | else | ||
568 | { | ||
569 | return null; | ||
570 | } | ||
571 | } | ||
572 | } | ||
573 | |||
574 | /// <summary> | ||
575 | /// Returns a specified inventory folder by its UUID | ||
576 | /// </summary> | ||
577 | /// <param name="folder">The UUID of the folder to be returned</param> | ||
578 | /// <returns>A class containing folder information</returns> | ||
579 | public InventoryFolderBase getInventoryFolder(UUID folder) | ||
580 | { | ||
581 | // TODO: Deep voodoo here. If you enable this code then | ||
582 | // multi region breaks. No idea why, but I figured it was | ||
583 | // better to leave multi region at this point. It does mean | ||
584 | // that you don't get to see system textures why creating | ||
585 | // clothes and the like. :( | ||
586 | lock (ds) | ||
587 | { | ||
588 | DataRow row = ds.Tables["inventoryfolders"].Rows.Find(folder.ToString()); | ||
589 | if (row != null) | ||
590 | { | ||
591 | return buildFolder(row); | ||
592 | } | ||
593 | else | ||
594 | { | ||
595 | return null; | ||
596 | } | ||
597 | } | ||
598 | } | ||
599 | |||
600 | /// <summary> | ||
601 | /// Creates a new inventory item based on item | ||
602 | /// </summary> | ||
603 | /// <param name="item">The item to be created</param> | ||
604 | public void addInventoryItem(InventoryItemBase item) | ||
605 | { | ||
606 | addItem(item, true); | ||
607 | } | ||
608 | |||
609 | /// <summary> | ||
610 | /// Updates an inventory item with item (updates based on ID) | ||
611 | /// </summary> | ||
612 | /// <param name="item">The updated item</param> | ||
613 | public void updateInventoryItem(InventoryItemBase item) | ||
614 | { | ||
615 | addItem(item, false); | ||
616 | } | ||
617 | |||
618 | /// <summary> | ||
619 | /// Delete an inventory item | ||
620 | /// </summary> | ||
621 | /// <param name="item">The item UUID</param> | ||
622 | public void deleteInventoryItem(UUID itemID) | ||
623 | { | ||
624 | lock (ds) | ||
625 | { | ||
626 | DataTable inventoryItemTable = ds.Tables["inventoryitems"]; | ||
627 | |||
628 | DataRow inventoryRow = inventoryItemTable.Rows.Find(itemID.ToString()); | ||
629 | if (inventoryRow != null) | ||
630 | { | ||
631 | inventoryRow.Delete(); | ||
632 | } | ||
633 | |||
634 | invItemsDa.Update(ds, "inventoryitems"); | ||
635 | } | ||
636 | } | ||
637 | |||
638 | public InventoryItemBase queryInventoryItem(UUID itemID) | ||
639 | { | ||
640 | return getInventoryItem(itemID); | ||
641 | } | ||
642 | |||
643 | public InventoryFolderBase queryInventoryFolder(UUID folderID) | ||
644 | { | ||
645 | return getInventoryFolder(folderID); | ||
646 | } | ||
647 | |||
648 | /// <summary> | ||
649 | /// Delete all items in the specified folder | ||
650 | /// </summary> | ||
651 | /// <param name="folderId">id of the folder, whose item content should be deleted</param> | ||
652 | /// <todo>this is horribly inefficient, but I don't want to ruin the overall structure of this implementation</todo> | ||
653 | private void deleteItemsInFolder(UUID folderId) | ||
654 | { | ||
655 | List<InventoryItemBase> items = getInventoryInFolder(folderId); | ||
656 | |||
657 | foreach (InventoryItemBase i in items) | ||
658 | deleteInventoryItem(i.ID); | ||
659 | } | ||
660 | |||
661 | /// <summary> | ||
662 | /// Adds a new folder specified by folder | ||
663 | /// </summary> | ||
664 | /// <param name="folder">The inventory folder</param> | ||
665 | public void addInventoryFolder(InventoryFolderBase folder) | ||
666 | { | ||
667 | addFolder(folder, true); | ||
668 | } | ||
669 | |||
670 | /// <summary> | ||
671 | /// Updates a folder based on its ID with folder | ||
672 | /// </summary> | ||
673 | /// <param name="folder">The inventory folder</param> | ||
674 | public void updateInventoryFolder(InventoryFolderBase folder) | ||
675 | { | ||
676 | addFolder(folder, false); | ||
677 | } | ||
678 | |||
679 | /// <summary> | ||
680 | /// Moves a folder based on its ID with folder | ||
681 | /// </summary> | ||
682 | /// <param name="folder">The inventory folder</param> | ||
683 | public void moveInventoryFolder(InventoryFolderBase folder) | ||
684 | { | ||
685 | moveFolder(folder); | ||
686 | } | ||
687 | |||
688 | /// <summary> | ||
689 | /// Delete a folder | ||
690 | /// </summary> | ||
691 | /// <remarks> | ||
692 | /// This will clean-up any child folders and child items as well | ||
693 | /// </remarks> | ||
694 | /// <param name="folderID">the folder UUID</param> | ||
695 | public void deleteInventoryFolder(UUID folderID) | ||
696 | { | ||
697 | lock (ds) | ||
698 | { | ||
699 | List<InventoryFolderBase> subFolders = getFolderHierarchy(folderID); | ||
700 | |||
701 | DataTable inventoryFolderTable = ds.Tables["inventoryfolders"]; | ||
702 | DataRow inventoryRow; | ||
703 | |||
704 | //Delete all sub-folders | ||
705 | foreach (InventoryFolderBase f in subFolders) | ||
706 | { | ||
707 | inventoryRow = inventoryFolderTable.Rows.Find(f.ID.ToString()); | ||
708 | if (inventoryRow != null) | ||
709 | { | ||
710 | deleteItemsInFolder(f.ID); | ||
711 | inventoryRow.Delete(); | ||
712 | } | ||
713 | } | ||
714 | |||
715 | //Delete the actual row | ||
716 | inventoryRow = inventoryFolderTable.Rows.Find(folderID.ToString()); | ||
717 | if (inventoryRow != null) | ||
718 | { | ||
719 | deleteItemsInFolder(folderID); | ||
720 | inventoryRow.Delete(); | ||
721 | } | ||
722 | |||
723 | invFoldersDa.Update(ds, "inventoryfolders"); | ||
724 | } | ||
725 | } | ||
726 | |||
727 | /*********************************************************************** | ||
728 | * | ||
729 | * Data Table definitions | ||
730 | * | ||
731 | **********************************************************************/ | ||
732 | |||
733 | protected void CreateDataSetMapping(IDataAdapter da, string tableName) | ||
734 | { | ||
735 | ITableMapping dbMapping = da.TableMappings.Add(tableName, tableName); | ||
736 | foreach (DataColumn col in ds.Tables[tableName].Columns) | ||
737 | { | ||
738 | dbMapping.ColumnMappings.Add(col.ColumnName, col.ColumnName); | ||
739 | } | ||
740 | } | ||
741 | |||
742 | /// <summary> | ||
743 | /// Create the "inventoryitems" table | ||
744 | /// </summary> | ||
745 | private static DataTable createInventoryItemsTable() | ||
746 | { | ||
747 | DataTable inv = new DataTable("inventoryitems"); | ||
748 | |||
749 | createCol(inv, "UUID", typeof (String)); //inventoryID | ||
750 | createCol(inv, "assetID", typeof (String)); | ||
751 | createCol(inv, "assetType", typeof (Int32)); | ||
752 | createCol(inv, "invType", typeof (Int32)); | ||
753 | createCol(inv, "parentFolderID", typeof (String)); | ||
754 | createCol(inv, "avatarID", typeof (String)); | ||
755 | createCol(inv, "creatorsID", typeof (String)); | ||
756 | |||
757 | createCol(inv, "inventoryName", typeof (String)); | ||
758 | createCol(inv, "inventoryDescription", typeof (String)); | ||
759 | // permissions | ||
760 | createCol(inv, "inventoryNextPermissions", typeof (Int32)); | ||
761 | createCol(inv, "inventoryCurrentPermissions", typeof (Int32)); | ||
762 | createCol(inv, "inventoryBasePermissions", typeof (Int32)); | ||
763 | createCol(inv, "inventoryEveryOnePermissions", typeof (Int32)); | ||
764 | createCol(inv, "inventoryGroupPermissions", typeof (Int32)); | ||
765 | |||
766 | // sale info | ||
767 | createCol(inv, "salePrice", typeof(Int32)); | ||
768 | createCol(inv, "saleType", typeof(Byte)); | ||
769 | |||
770 | // creation date | ||
771 | createCol(inv, "creationDate", typeof(Int32)); | ||
772 | |||
773 | // group info | ||
774 | createCol(inv, "groupID", typeof(String)); | ||
775 | createCol(inv, "groupOwned", typeof(Boolean)); | ||
776 | |||
777 | // Flags | ||
778 | createCol(inv, "flags", typeof(UInt32)); | ||
779 | |||
780 | inv.PrimaryKey = new DataColumn[] { inv.Columns["UUID"] }; | ||
781 | return inv; | ||
782 | } | ||
783 | |||
784 | /// <summary> | ||
785 | /// Creates the "inventoryfolders" table | ||
786 | /// </summary> | ||
787 | /// <returns></returns> | ||
788 | private static DataTable createInventoryFoldersTable() | ||
789 | { | ||
790 | DataTable fol = new DataTable("inventoryfolders"); | ||
791 | |||
792 | createCol(fol, "UUID", typeof (String)); //folderID | ||
793 | createCol(fol, "name", typeof (String)); | ||
794 | createCol(fol, "agentID", typeof (String)); | ||
795 | createCol(fol, "parentID", typeof (String)); | ||
796 | createCol(fol, "type", typeof (Int32)); | ||
797 | createCol(fol, "version", typeof (Int32)); | ||
798 | |||
799 | fol.PrimaryKey = new DataColumn[] {fol.Columns["UUID"]}; | ||
800 | return fol; | ||
801 | } | ||
802 | |||
803 | /// <summary> | ||
804 | /// | ||
805 | /// </summary> | ||
806 | /// <param name="da"></param> | ||
807 | /// <param name="conn"></param> | ||
808 | private void setupItemsCommands(SqliteDataAdapter da, SqliteConnection conn) | ||
809 | { | ||
810 | lock (ds) | ||
811 | { | ||
812 | da.InsertCommand = createInsertCommand("inventoryitems", ds.Tables["inventoryitems"]); | ||
813 | da.InsertCommand.Connection = conn; | ||
814 | |||
815 | da.UpdateCommand = createUpdateCommand("inventoryitems", "UUID=:UUID", ds.Tables["inventoryitems"]); | ||
816 | da.UpdateCommand.Connection = conn; | ||
817 | |||
818 | SqliteCommand delete = new SqliteCommand("delete from inventoryitems where UUID = :UUID"); | ||
819 | delete.Parameters.Add(createSqliteParameter("UUID", typeof(String))); | ||
820 | delete.Connection = conn; | ||
821 | da.DeleteCommand = delete; | ||
822 | } | ||
823 | } | ||
824 | |||
825 | /// <summary> | ||
826 | /// | ||
827 | /// </summary> | ||
828 | /// <param name="da"></param> | ||
829 | /// <param name="conn"></param> | ||
830 | private void setupFoldersCommands(SqliteDataAdapter da, SqliteConnection conn) | ||
831 | { | ||
832 | lock (ds) | ||
833 | { | ||
834 | da.InsertCommand = createInsertCommand("inventoryfolders", ds.Tables["inventoryfolders"]); | ||
835 | da.InsertCommand.Connection = conn; | ||
836 | |||
837 | da.UpdateCommand = createUpdateCommand("inventoryfolders", "UUID=:UUID", ds.Tables["inventoryfolders"]); | ||
838 | da.UpdateCommand.Connection = conn; | ||
839 | |||
840 | SqliteCommand delete = new SqliteCommand("delete from inventoryfolders where UUID = :UUID"); | ||
841 | delete.Parameters.Add(createSqliteParameter("UUID", typeof(String))); | ||
842 | delete.Connection = conn; | ||
843 | da.DeleteCommand = delete; | ||
844 | } | ||
845 | } | ||
846 | |||
847 | /// <summary> | ||
848 | /// | ||
849 | /// </summary> | ||
850 | /// <param name="row"></param> | ||
851 | /// <returns></returns> | ||
852 | private static InventoryFolderBase buildFolder(DataRow row) | ||
853 | { | ||
854 | InventoryFolderBase folder = new InventoryFolderBase(); | ||
855 | folder.ID = new UUID((string) row["UUID"]); | ||
856 | folder.Name = (string) row["name"]; | ||
857 | folder.Owner = new UUID((string) row["agentID"]); | ||
858 | folder.ParentID = new UUID((string) row["parentID"]); | ||
859 | folder.Type = Convert.ToInt16(row["type"]); | ||
860 | folder.Version = Convert.ToUInt16(row["version"]); | ||
861 | return folder; | ||
862 | } | ||
863 | |||
864 | /// <summary> | ||
865 | /// | ||
866 | /// </summary> | ||
867 | /// <param name="row"></param> | ||
868 | /// <param name="folder"></param> | ||
869 | private static void fillFolderRow(DataRow row, InventoryFolderBase folder) | ||
870 | { | ||
871 | row["UUID"] = folder.ID.ToString(); | ||
872 | row["name"] = folder.Name; | ||
873 | row["agentID"] = folder.Owner.ToString(); | ||
874 | row["parentID"] = folder.ParentID.ToString(); | ||
875 | row["type"] = folder.Type; | ||
876 | row["version"] = folder.Version; | ||
877 | } | ||
878 | |||
879 | /// <summary> | ||
880 | /// | ||
881 | /// </summary> | ||
882 | /// <param name="row"></param> | ||
883 | /// <param name="folder"></param> | ||
884 | private static void moveFolderRow(DataRow row, InventoryFolderBase folder) | ||
885 | { | ||
886 | row["UUID"] = folder.ID.ToString(); | ||
887 | row["parentID"] = folder.ParentID.ToString(); | ||
888 | } | ||
889 | |||
890 | public List<InventoryItemBase> fetchActiveGestures (UUID avatarID) | ||
891 | { | ||
892 | lock (ds) | ||
893 | { | ||
894 | List<InventoryItemBase> items = new List<InventoryItemBase>(); | ||
895 | |||
896 | DataTable inventoryItemTable = ds.Tables["inventoryitems"]; | ||
897 | string selectExp | ||
898 | = "avatarID = '" + avatarID + "' AND assetType = " + (int)AssetType.Gesture + " AND flags = 1"; | ||
899 | //m_log.DebugFormat("[SQL]: sql = " + selectExp); | ||
900 | DataRow[] rows = inventoryItemTable.Select(selectExp); | ||
901 | foreach (DataRow row in rows) | ||
902 | { | ||
903 | items.Add(buildItem(row)); | ||
904 | } | ||
905 | return items; | ||
906 | } | ||
907 | } | ||
908 | } | ||
909 | } | ||