diff --git a/.gitignore b/.gitignore index 91e9be50cf..6561718ce2 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,12 @@ *.dll.build *.dll *.log + +# Ignore .user and .suo files as these are user preference specific +# http://stackoverflow.com/questions/72298/should-i-add-the-visual-studio-suo-and-user-files-to-source-control +*.suo +*.user + *.VisualState.xml */*/obj */*/*/obj @@ -68,7 +74,6 @@ bin/crashes/ Examples/*.dll OpenSim.build OpenSim.sln -OpenSim.suo OpenSim.userprefs Prebuild/Prebuild.build Prebuild/Prebuild.sln diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 8ff55df3da..6fa116d27d 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -1,5 +1,4 @@ - <<<>>>>The following people have contributed to OpenSim (Thank you -for your effort!) +The following people have contributed to OpenSim (Thank you for your effort!) = Current OpenSim Developers (in very rough order of appearance) = These folks represent the current core team for OpenSim, and are the @@ -7,7 +6,7 @@ people that make the day to day of OpenSim happen. * justincc (OSVW Consulting, justincc.org) * chi11ken (Genkii) -* dahlia +* dahlia * Melanie Thielker * Diva (Crista Lopes, University of California, Irvine) * Dan Lake (Intel) @@ -58,13 +57,15 @@ where we are today. = Additional OpenSim Contributors = -These folks have contributed code patches to OpenSim to help make it +These folks have contributed code patches or content to OpenSimulator to help make it what it is today. -* aduffy70 * A_Biondi +* aduffy70 +* Ai Austin * alex_carnell * Alan Webb (IBM) +* Aleric * Allen Kerensky * BigFootAg * BlueWall Slade @@ -74,17 +75,20 @@ what it is today. * Chris Yeoh (IBM) * controlbreak * coyled +* ctrlaltdavid (David Rowe) * Daedius -* Dong Jun Lan (IBM) -* DoranZemlja * daTwitch * devalnor-#708 * dmiles (Daxtron Labs) +* Dong Jun Lan (IBM) +* DoranZemlja +* dr0b3rts * dslake (Intel) * FredoChaplin * Garmin Kawaguichi * Gerhard * Godfrey +* Greg C. * Grumly57 * GuduleLapointe * Ewe Loon @@ -101,10 +105,12 @@ what it is today. * jhurliman * John R Sohn (XenReborn) * jonc +* Jon Cundill * Junta Kohime * Kayne * Kevin Cozens * kinoc (Daxtron Labs) +* Kira * Kitto Flora * KittyLiu * Kurt Taylor (IBM) @@ -161,28 +167,24 @@ what it is today. * webmage (IBM) * Xantor * Y. Nitta +* YoshikoFazuku * YZh * Zackary Geers aka Kunnis Basiat * Zha Ewry * ziah - = LSL Devs = - * Alondria * CharlieO * Tedd * Melanie Thielker - = Testers = - * Ai Austin * CharlieO (LSL) * Ckrinke * openlifegrid.com - This software uses components from the following developers: * Sleepycat Software (Berkeley DB) * Aurora-Sim (http://aurora-sim.org) @@ -213,5 +215,3 @@ In addition, we would like to thank: * The Mono Project * The NANT Developers * Microsoft (.NET, MSSQL-Adapters) -*x - diff --git a/OpenSim/Addons/Groups/Hypergrid/GroupsServiceHGConnector.cs b/OpenSim/Addons/Groups/Hypergrid/GroupsServiceHGConnector.cs index 59fec6fa38..653dbac8bd 100644 --- a/OpenSim/Addons/Groups/Hypergrid/GroupsServiceHGConnector.cs +++ b/OpenSim/Addons/Groups/Hypergrid/GroupsServiceHGConnector.cs @@ -101,7 +101,7 @@ namespace OpenSim.Groups Dictionary sendData = new Dictionary(); if (GroupID != UUID.Zero) sendData["GroupID"] = GroupID.ToString(); - if (GroupName != null && GroupName != string.Empty) + if (!string.IsNullOrEmpty(GroupName)) sendData["Name"] = GroupsDataUtils.Sanitize(GroupName); sendData["RequestingAgentID"] = RequestingAgentID; @@ -275,7 +275,7 @@ namespace OpenSim.Groups //m_log.DebugFormat("[XXX]: reply was {0}", reply); - if (reply == string.Empty || reply == null) + if (string.IsNullOrEmpty(reply)) return null; Dictionary replyData = ServerUtils.ParseXmlResponse( diff --git a/OpenSim/Addons/Groups/Hypergrid/GroupsServiceHGConnectorModule.cs b/OpenSim/Addons/Groups/Hypergrid/GroupsServiceHGConnectorModule.cs index 4642b2adaf..7d4851652b 100644 --- a/OpenSim/Addons/Groups/Hypergrid/GroupsServiceHGConnectorModule.cs +++ b/OpenSim/Addons/Groups/Hypergrid/GroupsServiceHGConnectorModule.cs @@ -623,10 +623,13 @@ namespace OpenSim.Groups if (agent != null) break; } - if (agent == null) // oops - return AgentID.ToString(); + if (agent != null) + return Util.ProduceUserUniversalIdentifier(agent); + + // we don't know anything about this foreign user + // try asking the user management module, which may know more + return m_UserManagement.GetUserUUI(AgentID); - return Util.ProduceUserUniversalIdentifier(agent); } private string AgentUUIForOutside(string AgentIDStr) diff --git a/OpenSim/Addons/Groups/Hypergrid/HGGroupsServiceRobustConnector.cs b/OpenSim/Addons/Groups/Hypergrid/HGGroupsServiceRobustConnector.cs index 6f589226f2..d2bcba5c4b 100644 --- a/OpenSim/Addons/Groups/Hypergrid/HGGroupsServiceRobustConnector.cs +++ b/OpenSim/Addons/Groups/Hypergrid/HGGroupsServiceRobustConnector.cs @@ -112,7 +112,7 @@ namespace OpenSim.Groups m_GroupsService = service; } - public override byte[] Handle(string path, Stream requestData, + protected override byte[] ProcessRequest(string path, Stream requestData, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) { StreamReader sr = new StreamReader(requestData); diff --git a/OpenSim/Addons/Groups/Properties/AssemblyInfo.cs b/OpenSim/Addons/Groups/Properties/AssemblyInfo.cs index 5ccd7fea1b..16fe03b706 100644 --- a/OpenSim/Addons/Groups/Properties/AssemblyInfo.cs +++ b/OpenSim/Addons/Groups/Properties/AssemblyInfo.cs @@ -30,7 +30,7 @@ using Mono.Addins; // Build Number // Revision // -[assembly: AssemblyVersion("0.7.6.*")] +[assembly: AssemblyVersion("0.8.0.*")] [assembly: Addin("OpenSim.Groups", "0.1")] [assembly: AddinDependency("OpenSim", "0.5")] diff --git a/OpenSim/Addons/Groups/Remote/GroupsServiceRemoteConnector.cs b/OpenSim/Addons/Groups/Remote/GroupsServiceRemoteConnector.cs index 161ca0c4cc..ed419785e8 100644 --- a/OpenSim/Addons/Groups/Remote/GroupsServiceRemoteConnector.cs +++ b/OpenSim/Addons/Groups/Remote/GroupsServiceRemoteConnector.cs @@ -120,7 +120,7 @@ namespace OpenSim.Groups Dictionary sendData = new Dictionary(); if (GroupID != UUID.Zero) sendData["GroupID"] = GroupID.ToString(); - if (GroupName != null && GroupName != string.Empty) + if (!string.IsNullOrEmpty(GroupName)) sendData["Name"] = GroupsDataUtils.Sanitize(GroupName); sendData["RequestingAgentID"] = RequestingAgentID; diff --git a/OpenSim/Addons/Groups/Remote/GroupsServiceRobustConnector.cs b/OpenSim/Addons/Groups/Remote/GroupsServiceRobustConnector.cs index 7e55d3ce8a..249d974630 100644 --- a/OpenSim/Addons/Groups/Remote/GroupsServiceRobustConnector.cs +++ b/OpenSim/Addons/Groups/Remote/GroupsServiceRobustConnector.cs @@ -75,7 +75,7 @@ namespace OpenSim.Groups m_GroupsService = service; } - public override byte[] Handle(string path, Stream requestData, + protected override byte[] ProcessRequest(string path, Stream requestData, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) { StreamReader sr = new StreamReader(requestData); diff --git a/OpenSim/Addons/OfflineIM/OfflineIMRegionModule.cs b/OpenSim/Addons/OfflineIM/OfflineIMRegionModule.cs index 050ebd20f0..5ef068a251 100644 --- a/OpenSim/Addons/OfflineIM/OfflineIMRegionModule.cs +++ b/OpenSim/Addons/OfflineIM/OfflineIMRegionModule.cs @@ -261,6 +261,11 @@ namespace OpenSim.OfflineIM return m_OfflineIMService.StoreMessage(im, out reason); } + public void DeleteMessages(UUID userID) + { + m_OfflineIMService.DeleteMessages(userID); + } + #endregion } } diff --git a/OpenSim/Addons/OfflineIM/Properties/AssemblyInfo.cs b/OpenSim/Addons/OfflineIM/Properties/AssemblyInfo.cs index ffe8a3ec21..5d71edcb66 100644 --- a/OpenSim/Addons/OfflineIM/Properties/AssemblyInfo.cs +++ b/OpenSim/Addons/OfflineIM/Properties/AssemblyInfo.cs @@ -30,7 +30,7 @@ using Mono.Addins; // Build Number // Revision // -[assembly: AssemblyVersion("0.7.6.*")] +[assembly: AssemblyVersion("0.8.0.*")] [assembly: Addin("OpenSim.OfflineIM", "0.1")] [assembly: AddinDependency("OpenSim", "0.5")] diff --git a/OpenSim/Addons/OfflineIM/Remote/OfflineIMServiceRemoteConnector.cs b/OpenSim/Addons/OfflineIM/Remote/OfflineIMServiceRemoteConnector.cs index 69feb762f9..f6b17e5a5d 100644 --- a/OpenSim/Addons/OfflineIM/Remote/OfflineIMServiceRemoteConnector.cs +++ b/OpenSim/Addons/OfflineIM/Remote/OfflineIMServiceRemoteConnector.cs @@ -117,6 +117,14 @@ namespace OpenSim.OfflineIM return true; } + public void DeleteMessages(UUID userID) + { + Dictionary sendData = new Dictionary(); + sendData["UserID"] = userID; + + MakeRequest("DELETE", sendData); + } + #endregion diff --git a/OpenSim/Addons/OfflineIM/Remote/OfflineIMServiceRobustConnector.cs b/OpenSim/Addons/OfflineIM/Remote/OfflineIMServiceRobustConnector.cs index 2b3a01d32d..13b0e7e6a8 100644 --- a/OpenSim/Addons/OfflineIM/Remote/OfflineIMServiceRobustConnector.cs +++ b/OpenSim/Addons/OfflineIM/Remote/OfflineIMServiceRobustConnector.cs @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) Contributors, http://opensimulator.org/ * See CONTRIBUTORS.TXT for a full list of copyright holders. * @@ -75,7 +75,7 @@ namespace OpenSim.OfflineIM m_OfflineIMService = service; } - public override byte[] Handle(string path, Stream requestData, + protected override byte[] ProcessRequest(string path, Stream requestData, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) { StreamReader sr = new StreamReader(requestData); @@ -96,13 +96,14 @@ namespace OpenSim.OfflineIM string method = request["METHOD"].ToString(); request.Remove("METHOD"); - m_log.DebugFormat("[OfflineIM.V2.Handler]: {0}", method); switch (method) { case "GET": return HandleGet(request); case "STORE": return HandleStore(request); + case "DELETE": + return HandleDelete(request); } m_log.DebugFormat("[OFFLINE IM HANDLER]: unknown method request: {0}", method); } @@ -159,6 +160,21 @@ namespace OpenSim.OfflineIM return Util.UTF8NoBomEncoding.GetBytes(xmlString); } + byte[] HandleDelete(Dictionary request) + { + if (!request.ContainsKey("UserID")) + { + return FailureResult(); + } + else + { + UUID userID = new UUID(request["UserID"].ToString()); + m_OfflineIMService.DeleteMessages(userID); + + return SuccessResult(); + } + } + #region Helpers private void NullResult(Dictionary result, string reason) diff --git a/OpenSim/Addons/OfflineIM/Service/OfflineIMService.cs b/OpenSim/Addons/OfflineIM/Service/OfflineIMService.cs index 6ba022cd93..690c955660 100644 --- a/OpenSim/Addons/OfflineIM/Service/OfflineIMService.cs +++ b/OpenSim/Addons/OfflineIM/Service/OfflineIMService.cs @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) Contributors, http://opensimulator.org/ * See CONTRIBUTORS.TXT for a full list of copyright holders. * @@ -46,7 +46,7 @@ namespace OpenSim.OfflineIM { public class OfflineIMService : OfflineIMServiceBase, IOfflineIMService { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private const int MAX_IM = 25; private XmlSerializer m_serializer; @@ -91,7 +91,7 @@ namespace OpenSim.OfflineIM { reason = string.Empty; - // TODO Check limits + // Check limits UUID principalID = new UUID(im.toAgentID); long count = m_Database.GetCount("PrincipalID", principalID.ToString()); if (count >= MAX_IM) @@ -100,7 +100,7 @@ namespace OpenSim.OfflineIM return false; } - string imXml = string.Empty; + string imXml; using (MemoryStream mstream = new MemoryStream()) { XmlWriterSettings settings = new XmlWriterSettings(); @@ -110,22 +110,26 @@ namespace OpenSim.OfflineIM { m_serializer.Serialize(writer, im); writer.Flush(); - - mstream.Position = 0; - using (StreamReader sreader = new StreamReader(mstream)) - { - imXml = sreader.ReadToEnd(); - } } + + imXml = Util.UTF8.GetString(mstream.ToArray()); } OfflineIMData data = new OfflineIMData(); data.PrincipalID = principalID; + data.FromID = new UUID(im.fromAgentID); data.Data = new Dictionary(); data.Data["Message"] = imXml; return m_Database.Store(data); } + + public void DeleteMessages(UUID userID) + { + m_Database.Delete("PrincipalID", userID.ToString()); + m_Database.Delete("FromID", userID.ToString()); + } + } } diff --git a/OpenSim/ApplicationPlugins/LoadRegions/LoadRegionsPlugin.cs b/OpenSim/ApplicationPlugins/LoadRegions/LoadRegionsPlugin.cs index fcb6991969..1d63d2611e 100644 --- a/OpenSim/ApplicationPlugins/LoadRegions/LoadRegionsPlugin.cs +++ b/OpenSim/ApplicationPlugins/LoadRegions/LoadRegionsPlugin.cs @@ -115,6 +115,8 @@ namespace OpenSim.ApplicationPlugins.LoadRegions Environment.Exit(1); } + List createdScenes = new List(); + for (int i = 0; i < regionsToLoad.Length; i++) { IScene scene; @@ -123,17 +125,22 @@ namespace OpenSim.ApplicationPlugins.LoadRegions ")"); bool changed = m_openSim.PopulateRegionEstateInfo(regionsToLoad[i]); + m_openSim.CreateRegion(regionsToLoad[i], true, out scene); + createdScenes.Add(scene); + if (changed) - regionsToLoad[i].EstateSettings.Save(); - - if (scene != null) + regionsToLoad[i].EstateSettings.Save(); + } + + foreach (IScene scene in createdScenes) + { + scene.Start(); + + m_newRegionCreatedHandler = OnNewRegionCreated; + if (m_newRegionCreatedHandler != null) { - m_newRegionCreatedHandler = OnNewRegionCreated; - if (m_newRegionCreatedHandler != null) - { - m_newRegionCreatedHandler(scene); - } + m_newRegionCreatedHandler(scene); } } } diff --git a/OpenSim/ApplicationPlugins/LoadRegions/Properties/AssemblyInfo.cs b/OpenSim/ApplicationPlugins/LoadRegions/Properties/AssemblyInfo.cs index 1b6a3e1a8e..4968e7b45a 100644 --- a/OpenSim/ApplicationPlugins/LoadRegions/Properties/AssemblyInfo.cs +++ b/OpenSim/ApplicationPlugins/LoadRegions/Properties/AssemblyInfo.cs @@ -62,4 +62,4 @@ using System.Runtime.InteropServices; // by using the '*' as shown below: // [assembly: AssemblyVersion("0.7.6.*")] -[assembly : AssemblyVersion("0.7.6.*")] +[assembly : AssemblyVersion("0.8.0.*")] diff --git a/OpenSim/ApplicationPlugins/RegionModulesController/Properties/AssemblyInfo.cs b/OpenSim/ApplicationPlugins/RegionModulesController/Properties/AssemblyInfo.cs index 5683a72313..ddef158a36 100644 --- a/OpenSim/ApplicationPlugins/RegionModulesController/Properties/AssemblyInfo.cs +++ b/OpenSim/ApplicationPlugins/RegionModulesController/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; // Build Number // Revision // -[assembly: AssemblyVersion("0.7.6.*")] +[assembly: AssemblyVersion("0.8.0.*")] diff --git a/OpenSim/ApplicationPlugins/RemoteController/Properties/AssemblyInfo.cs b/OpenSim/ApplicationPlugins/RemoteController/Properties/AssemblyInfo.cs index a9d3f74ecd..dd34cc8f67 100644 --- a/OpenSim/ApplicationPlugins/RemoteController/Properties/AssemblyInfo.cs +++ b/OpenSim/ApplicationPlugins/RemoteController/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; // Build Number // Revision // -[assembly: AssemblyVersion("0.7.6.*")] +[assembly: AssemblyVersion("0.8.0.*")] diff --git a/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs b/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs index e50dac611e..cf2e037e26 100644 --- a/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs +++ b/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs @@ -28,6 +28,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Xml; using System.Net; @@ -51,6 +52,7 @@ using OpenSim.Services.Interfaces; using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo; using GridRegion = OpenSim.Services.Interfaces.GridRegion; using PermissionMask = OpenSim.Framework.PermissionMask; +using RegionInfo = OpenSim.Framework.RegionInfo; namespace OpenSim.ApplicationPlugins.RemoteController { @@ -149,6 +151,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController availableMethods["admin_create_user_email"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcCreateUserMethod); availableMethods["admin_exists_user"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcUserExistsMethod); availableMethods["admin_update_user"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcUpdateUserAccountMethod); + availableMethods["admin_authenticate_user"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcAuthenticateUserMethod); // Region state management availableMethods["admin_load_xml"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcLoadXMLMethod); @@ -161,6 +164,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController availableMethods["admin_acl_add"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcAccessListAdd); availableMethods["admin_acl_remove"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcAccessListRemove); availableMethods["admin_acl_list"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcAccessListList); + availableMethods["admin_estate_reload"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcEstateReload); // Misc availableMethods["admin_refresh_search"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcRefreshSearch); @@ -438,18 +442,26 @@ namespace OpenSim.ApplicationPlugins.RemoteController // k, (string)requestData[k], ((string)requestData[k]).Length); // } - CheckStringParameters(requestData, responseData, new string[] {"filename", "regionid"}); + CheckStringParameters(requestData, responseData, new string[] { "filename" }); CheckRegionParams(requestData, responseData); Scene scene = null; GetSceneFromRegionParams(requestData, responseData, out scene); - string file = (string)requestData["filename"]; - responseData["accepted"] = true; + if (scene != null) + { + string file = (string)requestData["filename"]; - LoadHeightmap(file, scene.RegionInfo.RegionID); + responseData["accepted"] = true; - responseData["success"] = true; + LoadHeightmap(file, scene.RegionInfo.RegionID); + + responseData["success"] = true; + } + else + { + responseData["success"] = false; + } m_log.Info("[RADMIN]: Load height maps request complete"); } @@ -463,23 +475,30 @@ namespace OpenSim.ApplicationPlugins.RemoteController // m_log.DebugFormat("[RADMIN]: Save Terrain: XmlRpc {0}", request.ToString()); - CheckStringParameters(requestData, responseData, new string[] { "filename", "regionid" }); + CheckStringParameters(requestData, responseData, new string[] { "filename" }); CheckRegionParams(requestData, responseData); - Scene region = null; - GetSceneFromRegionParams(requestData, responseData, out region); + Scene scene = null; + GetSceneFromRegionParams(requestData, responseData, out scene); - string file = (string)requestData["filename"]; - m_log.InfoFormat("[RADMIN]: Terrain Saving: {0}", file); + if (scene != null) + { + string file = (string)requestData["filename"]; + m_log.InfoFormat("[RADMIN]: Terrain Saving: {0}", file); - responseData["accepted"] = true; + responseData["accepted"] = true; - ITerrainModule terrainModule = region.RequestModuleInterface(); - if (null == terrainModule) throw new Exception("terrain module not available"); + ITerrainModule terrainModule = scene.RequestModuleInterface(); + if (null == terrainModule) throw new Exception("terrain module not available"); - terrainModule.SaveToFile(file); + terrainModule.SaveToFile(file); - responseData["success"] = true; + responseData["success"] = true; + } + else + { + responseData["success"] = false; + } m_log.Info("[RADMIN]: Save height maps request complete"); } @@ -831,6 +850,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController IScene newScene; m_application.CreateRegion(region, out newScene); + newScene.Start(); // If an access specification was provided, use it. // Otherwise accept the default. @@ -1225,7 +1245,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController { GridUserInfo userInfo = m_application.SceneManager.CurrentOrFirstScene.GridUserService.GetGridUserInfo(account.PrincipalID.ToString()); if (userInfo != null) - responseData["lastlogin"] = userInfo.Login; + responseData["lastlogin"] = Util.ToUnixTime(userInfo.Login); else responseData["lastlogin"] = 0; @@ -1394,6 +1414,139 @@ namespace OpenSim.ApplicationPlugins.RemoteController } } + /// + /// Authenticate an user. + /// + /// incoming XML RPC request + /// + /// XmlRpcAuthenticateUserMethod takes the following XMLRPC + /// parameters + /// + /// parameter namedescription + /// password + /// admin password as set in OpenSim.ini + /// user_firstname + /// avatar's first name + /// user_lastname + /// avatar's last name + /// user_password + /// MD5 hash of avatar's password + /// token_lifetime + /// the lifetime of the returned token (upper bounded to 30s) + /// + /// + /// XmlRpcAuthenticateUserMethod returns + /// + /// namedescription + /// success + /// true or false + /// token + /// the authentication token sent by OpenSim + /// error + /// error message if success is false + /// + /// + private void XmlRpcAuthenticateUserMethod(XmlRpcRequest request, XmlRpcResponse response, + IPEndPoint remoteClient) + { + m_log.Info("[RADMIN]: AuthenticateUser: new request"); + + var responseData = (Hashtable)response.Value; + var requestData = (Hashtable)request.Params[0]; + + lock (m_requestLock) + { + try + { + CheckStringParameters(requestData, responseData, new[] + { + "user_firstname", + "user_lastname", + "user_password", + "token_lifetime" + }); + + var firstName = (string)requestData["user_firstname"]; + var lastName = (string)requestData["user_lastname"]; + var password = (string)requestData["user_password"]; + + var scene = m_application.SceneManager.CurrentOrFirstScene; + + if (scene.Equals(null)) + { + m_log.Debug("scene does not exist"); + throw new Exception("Scene does not exist."); + } + + var scopeID = scene.RegionInfo.ScopeID; + var account = scene.UserAccountService.GetUserAccount(scopeID, firstName, lastName); + + if (account.Equals(null) || account.PrincipalID.Equals(UUID.Zero)) + { + m_log.DebugFormat("avatar {0} {1} does not exist", firstName, lastName); + throw new Exception(String.Format("avatar {0} {1} does not exist", firstName, lastName)); + } + + if (String.IsNullOrEmpty(password)) + { + m_log.DebugFormat("[RADMIN]: AuthenticateUser: no password provided for {0} {1}", firstName, + lastName); + throw new Exception(String.Format("no password provided for {0} {1}", firstName, + lastName)); + } + + int lifetime; + if (int.TryParse((string)requestData["token_lifetime"], NumberStyles.Integer, CultureInfo.InvariantCulture, out lifetime) == false) + { + m_log.DebugFormat("[RADMIN]: AuthenticateUser: no token lifetime provided for {0} {1}", firstName, + lastName); + throw new Exception(String.Format("no token lifetime provided for {0} {1}", firstName, + lastName)); + } + + // Upper bound on lifetime set to 30s. + if (lifetime > 30) + { + m_log.DebugFormat("[RADMIN]: AuthenticateUser: token lifetime longer than 30s for {0} {1}", firstName, + lastName); + throw new Exception(String.Format("token lifetime longer than 30s for {0} {1}", firstName, + lastName)); + } + + var authModule = scene.RequestModuleInterface(); + if (authModule == null) + { + m_log.Debug("[RADMIN]: AuthenticateUser: no authentication module loded"); + throw new Exception("no authentication module loaded"); + } + + var token = authModule.Authenticate(account.PrincipalID, password, lifetime); + if (String.IsNullOrEmpty(token)) + { + m_log.DebugFormat("[RADMIN]: AuthenticateUser: authentication failed for {0} {1}", firstName, + lastName); + throw new Exception(String.Format("authentication failed for {0} {1}", firstName, + lastName)); + } + + m_log.DebugFormat("[RADMIN]: AuthenticateUser: account for user {0} {1} identified with token {2}", + firstName, lastName, token); + + responseData["token"] = token; + responseData["success"] = true; + + } + catch (Exception e) + { + responseData["success"] = false; + responseData["error"] = e.Message; + throw e; + } + + m_log.Info("[RADMIN]: AuthenticateUser: request complete"); + } + } + /// /// Load an OAR file into a region.. /// @@ -1522,7 +1675,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController /// private void XmlRpcSaveOARMethod(XmlRpcRequest request, XmlRpcResponse response, IPEndPoint remoteClient) { - m_log.Info("[RADMIN]: Received Save OAR Administrator Request"); + m_log.Info("[RADMIN]: Received Save OAR Request"); Hashtable responseData = (Hashtable)response.Value; Hashtable requestData = (Hashtable)request.Params[0]; @@ -1568,8 +1721,14 @@ namespace OpenSim.ApplicationPlugins.RemoteController if (archiver != null) { + Guid requestId = Guid.NewGuid(); scene.EventManager.OnOarFileSaved += RemoteAdminOarSaveCompleted; - archiver.ArchiveRegion(filename, options); + + m_log.InfoFormat( + "[RADMIN]: Submitting save OAR request for {0} to file {1}, request ID {2}", + scene.Name, filename, requestId); + + archiver.ArchiveRegion(filename, requestId, options); lock (m_saveOarLock) Monitor.Wait(m_saveOarLock,5000); @@ -1590,12 +1749,16 @@ namespace OpenSim.ApplicationPlugins.RemoteController throw e; } - m_log.Info("[RADMIN]: Save OAR Administrator Request complete"); + m_log.Info("[RADMIN]: Save OAR Request complete"); } private void RemoteAdminOarSaveCompleted(Guid uuid, string name) { - m_log.DebugFormat("[RADMIN]: File processing complete for {0}", name); + if (name != "") + m_log.ErrorFormat("[RADMIN]: Saving of OAR file with request ID {0} failed with message {1}", uuid, name); + else + m_log.DebugFormat("[RADMIN]: Saved OAR file for request {0}", uuid); + lock (m_saveOarLock) Monitor.Pulse(m_saveOarLock); } @@ -1902,6 +2065,22 @@ namespace OpenSim.ApplicationPlugins.RemoteController m_log.Info("[RADMIN]: Access List List Request complete"); } + private void XmlRpcEstateReload(XmlRpcRequest request, XmlRpcResponse response, IPEndPoint remoteClient) + { + m_log.Info("[RADMIN]: Received Estate Reload Request"); + + Hashtable responseData = (Hashtable)response.Value; +// Hashtable requestData = (Hashtable)request.Params[0]; + + m_application.SceneManager.ForEachScene(s => + s.RegionInfo.EstateSettings = m_application.EstateDataService.LoadEstateSettings(s.RegionInfo.RegionID, false) + ); + + responseData["success"] = true; + + m_log.Info("[RADMIN]: Estate Reload Request complete"); + } + private void XmlRpcGetAgentsMethod(XmlRpcRequest request, XmlRpcResponse response, IPEndPoint remoteClient) { Hashtable responseData = (Hashtable)response.Value; @@ -2763,15 +2942,13 @@ namespace OpenSim.ApplicationPlugins.RemoteController /// private void ApplyNextOwnerPermissions(InventoryItemBase item) { - if (item.InvType == (int)InventoryType.Object && (item.CurrentPermissions & 7) != 0) + if (item.InvType == (int)InventoryType.Object) { - if ((item.CurrentPermissions & ((uint)PermissionMask.Copy >> 13)) == 0) - item.CurrentPermissions &= ~(uint)PermissionMask.Copy; - if ((item.CurrentPermissions & ((uint)PermissionMask.Transfer >> 13)) == 0) - item.CurrentPermissions &= ~(uint)PermissionMask.Transfer; - if ((item.CurrentPermissions & ((uint)PermissionMask.Modify >> 13)) == 0) - item.CurrentPermissions &= ~(uint)PermissionMask.Modify; + uint perms = item.CurrentPermissions; + PermissionsUtil.ApplyFoldedPermissions(item.CurrentPermissions, ref perms); + item.CurrentPermissions = perms; } + item.CurrentPermissions &= item.NextPermissions; item.BasePermissions &= item.NextPermissions; item.EveryOnePermissions &= item.NextPermissions; diff --git a/OpenSim/Capabilities/Caps.cs b/OpenSim/Capabilities/Caps.cs index 241fef3866..30a323e348 100644 --- a/OpenSim/Capabilities/Caps.cs +++ b/OpenSim/Capabilities/Caps.cs @@ -64,7 +64,11 @@ namespace OpenSim.Framework.Capabilities public string CapsObjectPath { get { return m_capsObjectPath; } } private CapsHandlers m_capsHandlers; - private Dictionary m_externalCapsHandlers; + + private Dictionary m_pollServiceHandlers + = new Dictionary(); + + private Dictionary m_externalCapsHandlers = new Dictionary(); private IHttpServer m_httpListener; private UUID m_agentID; @@ -134,7 +138,6 @@ namespace OpenSim.Framework.Capabilities m_agentID = agent; m_capsHandlers = new CapsHandlers(httpServer, httpListen, httpPort, (httpServer == null) ? false : httpServer.UseSSL); - m_externalCapsHandlers = new Dictionary(); m_regionName = regionName; } @@ -145,8 +148,29 @@ namespace OpenSim.Framework.Capabilities /// public void RegisterHandler(string capName, IRequestHandler handler) { - m_capsHandlers[capName] = handler; //m_log.DebugFormat("[CAPS]: Registering handler for \"{0}\": path {1}", capName, handler.Path); + m_capsHandlers[capName] = handler; + } + + public void RegisterPollHandler(string capName, PollServiceEventArgs pollServiceHandler) + { + m_pollServiceHandlers.Add(capName, pollServiceHandler); + + m_httpListener.AddPollServiceHTTPHandler(pollServiceHandler.Url, pollServiceHandler); + +// uint port = (MainServer.Instance == null) ? 0 : MainServer.Instance.Port; +// string protocol = "http"; +// string hostName = m_httpListenerHostName; +// +// if (MainServer.Instance.UseSSL) +// { +// hostName = MainServer.Instance.SSLCommonName; +// port = MainServer.Instance.SSLPort; +// protocol = "https"; +// } + +// RegisterHandler( +// capName, String.Format("{0}://{1}:{2}{3}", protocol, hostName, port, pollServiceHandler.Url)); } /// @@ -165,13 +189,70 @@ namespace OpenSim.Framework.Capabilities /// public void DeregisterHandlers() { - if (m_capsHandlers != null) + foreach (string capsName in m_capsHandlers.Caps) { - foreach (string capsName in m_capsHandlers.Caps) + m_capsHandlers.Remove(capsName); + } + + foreach (PollServiceEventArgs handler in m_pollServiceHandlers.Values) + { + m_httpListener.RemovePollServiceHTTPHandler("", handler.Url); + } + } + + public bool TryGetPollHandler(string name, out PollServiceEventArgs pollHandler) + { + return m_pollServiceHandlers.TryGetValue(name, out pollHandler); + } + + public Dictionary GetPollHandlers() + { + return new Dictionary(m_pollServiceHandlers); + } + + /// + /// Return an LLSD-serializable Hashtable describing the + /// capabilities and their handler details. + /// + /// If true, then exclude the seed cap. + public Hashtable GetCapsDetails(bool excludeSeed, List requestedCaps) + { + Hashtable caps = CapsHandlers.GetCapsDetails(excludeSeed, requestedCaps); + + lock (m_pollServiceHandlers) + { + foreach (KeyValuePair kvp in m_pollServiceHandlers) { - m_capsHandlers.Remove(capsName); + if (!requestedCaps.Contains(kvp.Key)) + continue; + + string hostName = m_httpListenerHostName; + uint port = (MainServer.Instance == null) ? 0 : MainServer.Instance.Port; + string protocol = "http"; + + if (MainServer.Instance.UseSSL) + { + hostName = MainServer.Instance.SSLCommonName; + port = MainServer.Instance.SSLPort; + protocol = "https"; + } + // + // caps.RegisterHandler("FetchInventoryDescendents2", String.Format("{0}://{1}:{2}{3}", protocol, hostName, port, capUrl)); + + caps[kvp.Key] = string.Format("{0}://{1}:{2}{3}", protocol, hostName, port, kvp.Value.Url); } } + + // Add the external too + foreach (KeyValuePair kvp in ExternalCapsHandlers) + { + if (!requestedCaps.Contains(kvp.Key)) + continue; + + caps[kvp.Key] = kvp.Value; + } + + return caps; } public void Activate() @@ -185,4 +266,4 @@ namespace OpenSim.Framework.Capabilities return m_capsActive.WaitOne(30000); } } -} +} \ No newline at end of file diff --git a/OpenSim/Capabilities/CapsHandlers.cs b/OpenSim/Capabilities/CapsHandlers.cs index 458272d10c..890df9091c 100644 --- a/OpenSim/Capabilities/CapsHandlers.cs +++ b/OpenSim/Capabilities/CapsHandlers.cs @@ -39,7 +39,7 @@ namespace OpenSim.Framework.Capabilities /// public class CapsHandlers { - private Dictionary m_capsHandlers = new Dictionary(); + private Dictionary m_capsHandlers = new Dictionary(); private IHttpServer m_httpListener; private string m_httpListenerHostName; private uint m_httpListenerPort; @@ -184,5 +184,17 @@ namespace OpenSim.Framework.Capabilities return caps; } + + /// + /// Returns a copy of the dictionary of all the HTTP cap handlers + /// + /// + /// The dictionary copy. The key is the capability name, the value is the HTTP handler. + /// + public Dictionary GetCapsHandlers() + { + lock (m_capsHandlers) + return new Dictionary(m_capsHandlers); + } } -} +} \ No newline at end of file diff --git a/OpenSim/Capabilities/Handlers/AvatarPickerSearch/AvatarPickerSearchHandler.cs b/OpenSim/Capabilities/Handlers/AvatarPickerSearch/AvatarPickerSearchHandler.cs new file mode 100644 index 0000000000..426174d252 --- /dev/null +++ b/OpenSim/Capabilities/Handlers/AvatarPickerSearch/AvatarPickerSearchHandler.cs @@ -0,0 +1,116 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.IO; +using System.Reflection; +using System.Web; +using log4net; +using Nini.Config; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Framework.Capabilities; +using OpenSim.Framework.Servers; +using OpenSim.Framework.Servers.HttpServer; +//using OpenSim.Region.Framework.Interfaces; +using OpenSim.Services.Interfaces; +using Caps = OpenSim.Framework.Capabilities.Caps; + +namespace OpenSim.Capabilities.Handlers +{ + public class AvatarPickerSearchHandler : BaseStreamHandler + { + private static readonly ILog m_log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private IPeople m_PeopleService; + + public AvatarPickerSearchHandler(string path, IPeople peopleService, string name, string description) + : base("GET", path, name, description) + { + m_PeopleService = peopleService; + } + + protected override byte[] ProcessRequest(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + // Try to parse the texture ID from the request URL + NameValueCollection query = HttpUtility.ParseQueryString(httpRequest.Url.Query); + string names = query.GetOne("names"); + string psize = query.GetOne("page_size"); + string pnumber = query.GetOne("page"); + + if (m_PeopleService == null) + return FailureResponse(names, (int)System.Net.HttpStatusCode.InternalServerError, httpResponse); + + if (string.IsNullOrEmpty(names) || names.Length < 3) + return FailureResponse(names, (int)System.Net.HttpStatusCode.BadRequest, httpResponse); + + m_log.DebugFormat("[AVATAR PICKER SEARCH]: search for {0}", names); + + int page_size = (string.IsNullOrEmpty(psize) ? 500 : Int32.Parse(psize)); + int page_number = (string.IsNullOrEmpty(pnumber) ? 1 : Int32.Parse(pnumber)); + + // Full content request + httpResponse.StatusCode = (int)System.Net.HttpStatusCode.OK; + //httpResponse.ContentLength = ??; + httpResponse.ContentType = "application/llsd+xml"; + + List users = m_PeopleService.GetUserData(names, page_size, page_number); + + LLSDAvatarPicker osdReply = new LLSDAvatarPicker(); + osdReply.next_page_url = httpRequest.RawUrl; + foreach (UserData u in users) + osdReply.agents.Array.Add(ConvertUserData(u)); + + string reply = LLSDHelpers.SerialiseLLSDReply(osdReply); + return System.Text.Encoding.UTF8.GetBytes(reply); + } + + private LLSDPerson ConvertUserData(UserData user) + { + LLSDPerson p = new LLSDPerson(); + p.legacy_first_name = user.FirstName; + p.legacy_last_name = user.LastName; + p.display_name = user.FirstName + " " + user.LastName; + if (user.LastName.StartsWith("@")) + p.username = user.FirstName.ToLower() + user.LastName.ToLower(); + else + p.username = user.FirstName.ToLower() + "." + user.LastName.ToLower(); + p.id = user.Id; + p.is_display_name_default = false; + return p; + } + + private byte[] FailureResponse(string names, int statuscode, IOSHttpResponse httpResponse) + { + m_log.Error("[AVATAR PICKER SEARCH]: Error searching for " + names); + httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound; + return System.Text.Encoding.UTF8.GetBytes(string.Empty); + } + } +} \ No newline at end of file diff --git a/OpenSim/Capabilities/Handlers/FetchInventory2/FetchInventory2Handler.cs b/OpenSim/Capabilities/Handlers/FetchInventory2/FetchInventory2Handler.cs index c305797c59..d1503ee15c 100644 --- a/OpenSim/Capabilities/Handlers/FetchInventory2/FetchInventory2Handler.cs +++ b/OpenSim/Capabilities/Handlers/FetchInventory2/FetchInventory2Handler.cs @@ -46,7 +46,7 @@ namespace OpenSim.Capabilities.Handlers { public class FetchInventory2Handler { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private IInventoryService m_inventoryService; @@ -121,4 +121,4 @@ namespace OpenSim.Capabilities.Handlers return llsdItem; } } -} +} \ No newline at end of file diff --git a/OpenSim/Capabilities/Handlers/GetTexture/GetTextureHandler.cs b/OpenSim/Capabilities/Handlers/GetTexture/GetTextureHandler.cs index aa6203b3a3..f3efb53891 100644 --- a/OpenSim/Capabilities/Handlers/GetTexture/GetTextureHandler.cs +++ b/OpenSim/Capabilities/Handlers/GetTexture/GetTextureHandler.cs @@ -85,7 +85,7 @@ namespace OpenSim.Capabilities.Handlers // m_log.DebugFormat("[GETTEXTURE]: Received request for texture id {0}", textureID); string[] formats; - if (format != null && format != string.Empty) + if (!string.IsNullOrEmpty(format)) { formats = new string[1] { format.ToLower() }; } @@ -174,6 +174,7 @@ namespace OpenSim.Capabilities.Handlers newTexture.Flags = AssetFlags.Collectable; newTexture.Temporary = true; + newTexture.Local = true; m_assetService.Store(newTexture); WriteTextureData(request, response, newTexture, format); return true; diff --git a/OpenSim/Capabilities/Handlers/Properties/AssemblyInfo.cs b/OpenSim/Capabilities/Handlers/Properties/AssemblyInfo.cs index f8f63f437b..0cf8bf4093 100644 --- a/OpenSim/Capabilities/Handlers/Properties/AssemblyInfo.cs +++ b/OpenSim/Capabilities/Handlers/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; // Build Number // Revision // -[assembly: AssemblyVersion("0.7.6.*")] +[assembly: AssemblyVersion("0.8.0.*")] diff --git a/OpenSim/Capabilities/Handlers/UploadBakedTexture/UploadBakedTextureServerConnector.cs b/OpenSim/Capabilities/Handlers/UploadBakedTexture/UploadBakedTextureServerConnector.cs new file mode 100644 index 0000000000..10ea8eefe6 --- /dev/null +++ b/OpenSim/Capabilities/Handlers/UploadBakedTexture/UploadBakedTextureServerConnector.cs @@ -0,0 +1,76 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using Nini.Config; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Base; +using OpenMetaverse; + +namespace OpenSim.Capabilities.Handlers +{ + public class UploadBakedTextureServerConnector : ServiceConnector + { + private IAssetService m_AssetService; + private string m_ConfigName = "CapsService"; + + public UploadBakedTextureServerConnector(IConfigSource config, IHttpServer server, string configName) : + base(config, server, configName) + { + if (configName != String.Empty) + m_ConfigName = configName; + + IConfig serverConfig = config.Configs[m_ConfigName]; + if (serverConfig == null) + throw new Exception(String.Format("No section '{0}' in config file", m_ConfigName)); + + string assetService = serverConfig.GetString("AssetService", String.Empty); + + if (assetService == String.Empty) + throw new Exception("No AssetService in config file"); + + Object[] args = new Object[] { config }; + m_AssetService = + ServerUtils.LoadPlugin(assetService, args); + + if (m_AssetService == null) + throw new Exception(String.Format("Failed to load AssetService from {0}; config is {1}", assetService, m_ConfigName)); + + // NEED TO FIX THIS + OpenSim.Framework.Capabilities.Caps caps = new OpenSim.Framework.Capabilities.Caps(server, "", server.Port, "", UUID.Zero, ""); + server.AddStreamHandler(new RestStreamHandler( + "POST", + "/CAPS/UploadBakedTexture/", + new UploadBakedTextureHandler(caps, m_AssetService, true).UploadBakedTexture, + "UploadBakedTexture", + "Upload Baked Texture Capability")); + + } + } +} \ No newline at end of file diff --git a/OpenSim/Capabilities/LLSDAvatarPicker.cs b/OpenSim/Capabilities/LLSDAvatarPicker.cs new file mode 100644 index 0000000000..d0b3f3a5ec --- /dev/null +++ b/OpenSim/Capabilities/LLSDAvatarPicker.cs @@ -0,0 +1,51 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using OpenMetaverse; + +namespace OpenSim.Framework.Capabilities +{ + [OSDMap] + public class LLSDAvatarPicker + { + public string next_page_url; + // an array of LLSDPerson + public OSDArray agents = new OSDArray(); + } + + [OSDMap] + public class LLSDPerson + { + public string username; + public string display_name; + //'display_name_next_update':d"1970-01-01T00:00:00Z" + public string legacy_first_name; + public string legacy_last_name; + public UUID id; + public bool is_display_name_default; + } +} \ No newline at end of file diff --git a/OpenSim/Capabilities/LLSDStreamHandler.cs b/OpenSim/Capabilities/LLSDStreamHandler.cs index 5df24b2cc1..4fa1153cd6 100644 --- a/OpenSim/Capabilities/LLSDStreamHandler.cs +++ b/OpenSim/Capabilities/LLSDStreamHandler.cs @@ -48,7 +48,7 @@ namespace OpenSim.Framework.Capabilities m_method = method; } - public override byte[] Handle(string path, Stream request, + protected override byte[] ProcessRequest(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) { //Encoding encoding = Util.UTF8; diff --git a/OpenSim/Data/IGridUserData.cs b/OpenSim/Data/IGridUserData.cs index e15a1f88a5..9afa477f30 100644 --- a/OpenSim/Data/IGridUserData.cs +++ b/OpenSim/Data/IGridUserData.cs @@ -50,6 +50,7 @@ namespace OpenSim.Data public interface IGridUserData { GridUserData Get(string userID); + GridUserData[] GetAll(string query); bool Store(GridUserData data); } } \ No newline at end of file diff --git a/OpenSim/Data/IHGTravelingData.cs b/OpenSim/Data/IHGTravelingData.cs new file mode 100644 index 0000000000..452af7bc50 --- /dev/null +++ b/OpenSim/Data/IHGTravelingData.cs @@ -0,0 +1,59 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using OpenMetaverse; +using OpenSim.Framework; + +namespace OpenSim.Data +{ + // This MUST be a ref type! + public class HGTravelingData + { + public UUID SessionID; + public UUID UserID; + public Dictionary Data; + + public HGTravelingData() + { + Data = new Dictionary(); + } + } + + /// + /// An interface for connecting to the user grid datastore + /// + public interface IHGTravelingData + { + HGTravelingData Get(UUID sessionID); + HGTravelingData[] GetSessions(UUID userID); + bool Store(HGTravelingData data); + bool Delete(UUID sessionID); + void DeleteOld(); + } +} \ No newline at end of file diff --git a/OpenSim/Data/IOfflineIMData.cs b/OpenSim/Data/IOfflineIMData.cs index e780304ec8..58501a3b7d 100644 --- a/OpenSim/Data/IOfflineIMData.cs +++ b/OpenSim/Data/IOfflineIMData.cs @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) Contributors, http://opensimulator.org/ * See CONTRIBUTORS.TXT for a full list of copyright holders. * @@ -34,6 +34,7 @@ namespace OpenSim.Data public class OfflineIMData { public UUID PrincipalID; + public UUID FromID; public Dictionary Data; } diff --git a/OpenSim/Data/IProfilesData.cs b/OpenSim/Data/IProfilesData.cs new file mode 100644 index 0000000000..0de7f68af9 --- /dev/null +++ b/OpenSim/Data/IProfilesData.cs @@ -0,0 +1,56 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using OpenSim.Framework; + +namespace OpenSim.Data +{ + + public interface IProfilesData + { + OSDArray GetClassifiedRecords(UUID creatorId); + bool UpdateClassifiedRecord(UserClassifiedAdd ad, ref string result); + bool DeleteClassifiedRecord(UUID recordId); + OSDArray GetAvatarPicks(UUID avatarId); + UserProfilePick GetPickInfo(UUID avatarId, UUID pickId); + bool UpdatePicksRecord(UserProfilePick pick); + bool DeletePicksRecord(UUID pickId); + bool GetAvatarNotes(ref UserProfileNotes note); + bool UpdateAvatarNotes(ref UserProfileNotes note, ref string result); + bool GetAvatarProperties(ref UserProfileProperties props, ref string result); + bool UpdateAvatarProperties(ref UserProfileProperties props, ref string result); + bool UpdateAvatarInterests(UserProfileProperties up, ref string result); + bool GetClassifiedInfo(ref UserClassifiedAdd ad, ref string result); + bool GetUserAppData(ref UserAppData props, ref string result); + bool SetUserAppData(UserAppData props, ref string result); + OSDArray GetUserImageAssets(UUID avatarId); + } +} + diff --git a/OpenSim/Data/IRegionData.cs b/OpenSim/Data/IRegionData.cs index 70e10653de..463c621859 100644 --- a/OpenSim/Data/IRegionData.cs +++ b/OpenSim/Data/IRegionData.cs @@ -81,6 +81,7 @@ namespace OpenSim.Data bool Delete(UUID regionID); List GetDefaultRegions(UUID scopeID); + List GetDefaultHypergridRegions(UUID scopeID); List GetFallbackRegions(UUID scopeID, int x, int y); List GetHyperlinks(UUID scopeID); } diff --git a/OpenSim/Data/MSSQL/MSSQLGridUserData.cs b/OpenSim/Data/MSSQL/MSSQLGridUserData.cs index 1870273616..fd52122cb0 100644 --- a/OpenSim/Data/MSSQL/MSSQLGridUserData.cs +++ b/OpenSim/Data/MSSQL/MSSQLGridUserData.cs @@ -50,7 +50,7 @@ namespace OpenSim.Data.MSSQL { } - public GridUserData Get(string userID) + public new GridUserData Get(string userID) { GridUserData[] ret = Get("UserID", userID); @@ -60,5 +60,10 @@ namespace OpenSim.Data.MSSQL return ret[0]; } + public GridUserData[] GetAll(string userID) + { + return base.Get(String.Format("UserID LIKE '{0}%'", userID)); + } + } } diff --git a/OpenSim/Data/MSSQL/MSSQLRegionData.cs b/OpenSim/Data/MSSQL/MSSQLRegionData.cs index 0d89706566..c0589dfb49 100644 --- a/OpenSim/Data/MSSQL/MSSQLRegionData.cs +++ b/OpenSim/Data/MSSQL/MSSQLRegionData.cs @@ -315,6 +315,11 @@ namespace OpenSim.Data.MSSQL return Get((int)RegionFlags.DefaultRegion, scopeID); } + public List GetDefaultHypergridRegions(UUID scopeID) + { + return Get((int)RegionFlags.DefaultHGRegion, scopeID); + } + public List GetFallbackRegions(UUID scopeID, int x, int y) { List regions = Get((int)RegionFlags.FallbackRegion, scopeID); diff --git a/OpenSim/Data/MSSQL/MSSQLSimulationData.cs b/OpenSim/Data/MSSQL/MSSQLSimulationData.cs index 00af3a01e2..0d09be6361 100644 --- a/OpenSim/Data/MSSQL/MSSQLSimulationData.cs +++ b/OpenSim/Data/MSSQL/MSSQLSimulationData.cs @@ -351,7 +351,7 @@ IF EXISTS (SELECT UUID FROM prims WHERE UUID = @UUID) ScriptAccessPin = @ScriptAccessPin, AllowedDrop = @AllowedDrop, DieAtEdge = @DieAtEdge, SalePrice = @SalePrice, SaleType = @SaleType, ColorR = @ColorR, ColorG = @ColorG, ColorB = @ColorB, ColorA = @ColorA, ParticleSystem = @ParticleSystem, ClickAction = @ClickAction, Material = @Material, CollisionSound = @CollisionSound, CollisionSoundVolume = @CollisionSoundVolume, PassTouches = @PassTouches, - LinkNumber = @LinkNumber, MediaURL = @MediaURL, DynAttrs = @DynAttrs, + LinkNumber = @LinkNumber, MediaURL = @MediaURL, AttachedPosX = @AttachedPosX, AttachedPosY = @AttachedPosY, AttachedPosZ = @AttachedPosZ, DynAttrs = @DynAttrs, PhysicsShapeType = @PhysicsShapeType, Density = @Density, GravityModifier = @GravityModifier, Friction = @Friction, Restitution = @Restitution WHERE UUID = @UUID END @@ -367,7 +367,7 @@ ELSE PayPrice, PayButton1, PayButton2, PayButton3, PayButton4, LoopedSound, LoopedSoundGain, TextureAnimation, OmegaX, OmegaY, OmegaZ, CameraEyeOffsetX, CameraEyeOffsetY, CameraEyeOffsetZ, CameraAtOffsetX, CameraAtOffsetY, CameraAtOffsetZ, ForceMouselook, ScriptAccessPin, AllowedDrop, DieAtEdge, SalePrice, SaleType, ColorR, ColorG, ColorB, ColorA, - ParticleSystem, ClickAction, Material, CollisionSound, CollisionSoundVolume, PassTouches, LinkNumber, MediaURL, DynAttrs, + ParticleSystem, ClickAction, Material, CollisionSound, CollisionSoundVolume, PassTouches, LinkNumber, MediaURL, AttachedPosX, AttachedPosY, AttachedPosZ, DynAttrs, PhysicsShapeType, Density, GravityModifier, Friction, Restitution ) VALUES ( @UUID, @CreationDate, @Name, @Text, @Description, @SitName, @TouchName, @ObjectFlags, @OwnerMask, @NextOwnerMask, @GroupMask, @@ -378,7 +378,7 @@ ELSE @PayPrice, @PayButton1, @PayButton2, @PayButton3, @PayButton4, @LoopedSound, @LoopedSoundGain, @TextureAnimation, @OmegaX, @OmegaY, @OmegaZ, @CameraEyeOffsetX, @CameraEyeOffsetY, @CameraEyeOffsetZ, @CameraAtOffsetX, @CameraAtOffsetY, @CameraAtOffsetZ, @ForceMouselook, @ScriptAccessPin, @AllowedDrop, @DieAtEdge, @SalePrice, @SaleType, @ColorR, @ColorG, @ColorB, @ColorA, - @ParticleSystem, @ClickAction, @Material, @CollisionSound, @CollisionSoundVolume, @PassTouches, @LinkNumber, @MediaURL, @DynAttrs, + @ParticleSystem, @ClickAction, @Material, @CollisionSound, @CollisionSoundVolume, @PassTouches, @LinkNumber, @MediaURL, @AttachedPosX, @AttachedPosY, @AttachedPosZ, @DynAttrs, @PhysicsShapeType, @Density, @GravityModifier, @Friction, @Restitution ) END"; @@ -1695,6 +1695,12 @@ VALUES if (!(primRow["MediaURL"] is System.DBNull)) prim.MediaUrl = (string)primRow["MediaURL"]; + if (!(primRow["AttachedPosX"] is System.DBNull)) + prim.AttachedPos = new Vector3( + Convert.ToSingle(primRow["AttachedPosX"]), + Convert.ToSingle(primRow["AttachedPosY"]), + Convert.ToSingle(primRow["AttachedPosZ"])); + if (!(primRow["DynAttrs"] is System.DBNull)) prim.DynAttrs = DAMap.FromXml((string)primRow["DynAttrs"]); else @@ -2099,8 +2105,11 @@ VALUES parameters.Add(_Database.CreateParameter("PassTouches", 0)); parameters.Add(_Database.CreateParameter("LinkNumber", prim.LinkNum)); parameters.Add(_Database.CreateParameter("MediaURL", prim.MediaUrl)); - - if (prim.DynAttrs.Count > 0) + parameters.Add(_Database.CreateParameter("AttachedPosX", prim.AttachedPos.X)); + parameters.Add(_Database.CreateParameter("AttachedPosY", prim.AttachedPos.Y)); + parameters.Add(_Database.CreateParameter("AttachedPosZ", prim.AttachedPos.Z)); + + if (prim.DynAttrs.CountNamespaces > 0) parameters.Add(_Database.CreateParameter("DynAttrs", prim.DynAttrs.ToXml())); else parameters.Add(_Database.CreateParameter("DynAttrs", null)); diff --git a/OpenSim/Data/MSSQL/Properties/AssemblyInfo.cs b/OpenSim/Data/MSSQL/Properties/AssemblyInfo.cs index 9bc580e76a..38bb8686e3 100644 --- a/OpenSim/Data/MSSQL/Properties/AssemblyInfo.cs +++ b/OpenSim/Data/MSSQL/Properties/AssemblyInfo.cs @@ -61,5 +61,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: -[assembly : AssemblyVersion("0.7.6.*")] +[assembly : AssemblyVersion("0.8.0.*")] diff --git a/OpenSim/Data/MSSQL/Resources/RegionStore.migrations b/OpenSim/Data/MSSQL/Resources/RegionStore.migrations index 4549801218..bb898845b4 100644 --- a/OpenSim/Data/MSSQL/Resources/RegionStore.migrations +++ b/OpenSim/Data/MSSQL/Resources/RegionStore.migrations @@ -1168,3 +1168,15 @@ ALTER TABLE prims ADD `Friction` double NOT NULL default '0.6'; ALTER TABLE prims ADD `Restitution` double NOT NULL default '0.5'; COMMIT + +:VERSION 40 #---------------- Save Attachment info + +BEGIN TRANSACTION + +ALTER TABLE prims ADD AttachedPosX float(53) default 0.0; +ALTER TABLE prims ADD AttachedPosY float(53) default 0.0; +ALTER TABLE prims ADD AttachedPosZ float(53) default 0.0; +ALTER TABLE primshapes ADD LastAttachPoint int not null default 0; + +COMMIT + diff --git a/OpenSim/Data/MySQL/MySQLAssetData.cs b/OpenSim/Data/MySQL/MySQLAssetData.cs index 21dd5aa52b..59cc22a464 100644 --- a/OpenSim/Data/MySQL/MySQLAssetData.cs +++ b/OpenSim/Data/MySQL/MySQLAssetData.cs @@ -142,7 +142,8 @@ namespace OpenSim.Data.MySQL } catch (Exception e) { - m_log.Error("[ASSETS DB]: MySql failure fetching asset " + assetID + ": " + e.Message); + m_log.Error( + string.Format("[ASSETS DB]: MySql failure fetching asset {0}. Exception ", assetID), e); } } } @@ -243,10 +244,11 @@ namespace OpenSim.Data.MySQL } catch (Exception e) { - m_log.ErrorFormat( - "[ASSETS DB]: " + - "MySql failure updating access_time for asset {0} with name {1}" + Environment.NewLine + e.ToString() - + Environment.NewLine + "Attempting reconnection", asset.FullID, asset.Name); + m_log.Error( + string.Format( + "[ASSETS DB]: Failure updating access_time for asset {0} with name {1}. Exception ", + asset.FullID, asset.Name), + e); } } } @@ -286,8 +288,8 @@ namespace OpenSim.Data.MySQL } catch (Exception e) { - m_log.ErrorFormat( - "[ASSETS DB]: MySql failure fetching asset {0}" + Environment.NewLine + e.ToString(), uuid); + m_log.Error( + string.Format("[ASSETS DB]: MySql failure fetching asset {0}. Exception ", uuid), e); } } } @@ -346,7 +348,11 @@ namespace OpenSim.Data.MySQL } catch (Exception e) { - m_log.Error("[ASSETS DB]: MySql failure fetching asset set" + Environment.NewLine + e.ToString()); + m_log.Error( + string.Format( + "[ASSETS DB]: MySql failure fetching asset set from {0}, count {1}. Exception ", + start, count), + e); } } } diff --git a/OpenSim/Data/MySQL/MySQLGridUserData.cs b/OpenSim/Data/MySQL/MySQLGridUserData.cs index a9ce94d33c..00560c11db 100644 --- a/OpenSim/Data/MySQL/MySQLGridUserData.cs +++ b/OpenSim/Data/MySQL/MySQLGridUserData.cs @@ -46,7 +46,7 @@ namespace OpenSim.Data.MySQL public MySQLGridUserData(string connectionString, string realm) : base(connectionString, realm, "GridUserStore") {} - public GridUserData Get(string userID) + public new GridUserData Get(string userID) { GridUserData[] ret = Get("UserID", userID); @@ -56,6 +56,9 @@ namespace OpenSim.Data.MySQL return ret[0]; } - + public GridUserData[] GetAll(string userID) + { + return base.Get(String.Format("UserID LIKE '{0}%'", userID)); + } } } \ No newline at end of file diff --git a/OpenSim/Data/MySQL/MySQLGroupsData.cs b/OpenSim/Data/MySQL/MySQLGroupsData.cs index 2a1bd6c48e..03182849c7 100644 --- a/OpenSim/Data/MySQL/MySQLGroupsData.cs +++ b/OpenSim/Data/MySQL/MySQLGroupsData.cs @@ -88,7 +88,7 @@ namespace OpenSim.Data.MySQL if (string.IsNullOrEmpty(pattern)) pattern = "1 ORDER BY Name LIMIT 100"; else - pattern = string.Format("Name LIKE %{0}% ORDER BY Name LIMIT 100", pattern); + pattern = string.Format("Name LIKE '%{0}%' ORDER BY Name LIMIT 100", pattern); return m_Groups.Get(pattern); } diff --git a/OpenSim/Data/MySQL/MySQLHGTravelData.cs b/OpenSim/Data/MySQL/MySQLHGTravelData.cs new file mode 100644 index 0000000000..e81b880c95 --- /dev/null +++ b/OpenSim/Data/MySQL/MySQLHGTravelData.cs @@ -0,0 +1,80 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Data; +using System.Reflection; +using System.Threading; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; +using MySql.Data.MySqlClient; + +namespace OpenSim.Data.MySQL +{ + /// + /// A MySQL Interface for user grid data + /// + public class MySQLHGTravelData : MySQLGenericTableHandler, IHGTravelingData + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public MySQLHGTravelData(string connectionString, string realm) : base(connectionString, realm, "HGTravelStore") { } + + public HGTravelingData Get(UUID sessionID) + { + HGTravelingData[] ret = Get("SessionID", sessionID.ToString()); + + if (ret.Length == 0) + return null; + + return ret[0]; + } + + public HGTravelingData[] GetSessions(UUID userID) + { + return base.Get("UserID", userID.ToString()); + } + + public bool Delete(UUID sessionID) + { + return Delete("SessionID", sessionID.ToString()); + } + + public void DeleteOld() + { + using (MySqlCommand cmd = new MySqlCommand()) + { + cmd.CommandText = String.Format("delete from {0} where TMStamp < NOW() - INTERVAL 2 DAY", m_Realm); + + ExecuteNonQuery(cmd); + } + + } + } +} \ No newline at end of file diff --git a/OpenSim/Data/MySQL/MySQLOfflineIMData.cs b/OpenSim/Data/MySQL/MySQLOfflineIMData.cs index 252f358b7c..bafd204a32 100644 --- a/OpenSim/Data/MySQL/MySQLOfflineIMData.cs +++ b/OpenSim/Data/MySQL/MySQLOfflineIMData.cs @@ -47,13 +47,10 @@ namespace OpenSim.Data.MySQL public void DeleteOld() { - uint now = (uint)Util.UnixTimeSinceEpoch(); - using (MySqlCommand cmd = new MySqlCommand()) { - cmd.CommandText = String.Format("delete from {0} where TMStamp < ?tstamp", m_Realm); - cmd.Parameters.AddWithValue("?tstamp", now - 14 * 24 * 60 * 60); // > 2 weeks old - + cmd.CommandText = String.Format("delete from {0} where TMStamp < NOW() - INTERVAL 2 WEEK", m_Realm); + ExecuteNonQuery(cmd); } diff --git a/OpenSim/Data/MySQL/MySQLRegionData.cs b/OpenSim/Data/MySQL/MySQLRegionData.cs index a2d4ae4647..2ad7590759 100644 --- a/OpenSim/Data/MySQL/MySQLRegionData.cs +++ b/OpenSim/Data/MySQL/MySQLRegionData.cs @@ -310,6 +310,11 @@ namespace OpenSim.Data.MySQL return Get((int)RegionFlags.DefaultRegion, scopeID); } + public List GetDefaultHypergridRegions(UUID scopeID) + { + return Get((int)RegionFlags.DefaultHGRegion, scopeID); + } + public List GetFallbackRegions(UUID scopeID, int x, int y) { List regions = Get((int)RegionFlags.FallbackRegion, scopeID); diff --git a/OpenSim/Data/MySQL/MySQLSimulationData.cs b/OpenSim/Data/MySQL/MySQLSimulationData.cs index 537ec850d3..1b3e81e767 100644 --- a/OpenSim/Data/MySQL/MySQLSimulationData.cs +++ b/OpenSim/Data/MySQL/MySQLSimulationData.cs @@ -174,7 +174,8 @@ namespace OpenSim.Data.MySQL "CollisionSound, CollisionSoundVolume, " + "PassTouches, " + "PassCollisions, " + - "LinkNumber, MediaURL, KeyframeMotion, " + + "LinkNumber, MediaURL, KeyframeMotion, AttachedPosX, " + + "AttachedPosY, AttachedPosZ, " + "PhysicsShapeType, Density, GravityModifier, " + "Friction, Restitution, Vehicle, DynAttrs " + ") values (" + "?UUID, " + @@ -209,7 +210,8 @@ namespace OpenSim.Data.MySQL "?ColorB, ?ColorA, ?ParticleSystem, " + "?ClickAction, ?Material, ?CollisionSound, " + "?CollisionSoundVolume, ?PassTouches, ?PassCollisions, " + - "?LinkNumber, ?MediaURL, ?KeyframeMotion, " + + "?LinkNumber, ?MediaURL, ?KeyframeMotion, ?AttachedPosX, " + + "?AttachedPosY, ?AttachedPosZ, " + "?PhysicsShapeType, ?Density, ?GravityModifier, " + "?Friction, ?Restitution, ?Vehicle, ?DynAttrs)"; @@ -228,7 +230,7 @@ namespace OpenSim.Data.MySQL "PathTaperX, PathTaperY, PathTwist, " + "PathTwistBegin, ProfileBegin, ProfileEnd, " + "ProfileCurve, ProfileHollow, Texture, " + - "ExtraParams, State, Media) " + + "ExtraParams, State, LastAttachPoint, Media) " + "values (?UUID, " + "?Shape, ?ScaleX, ?ScaleY, ?ScaleZ, " + "?PCode, ?PathBegin, ?PathEnd, " + @@ -240,7 +242,7 @@ namespace OpenSim.Data.MySQL "?PathTwistBegin, ?ProfileBegin, " + "?ProfileEnd, ?ProfileCurve, " + "?ProfileHollow, ?Texture, ?ExtraParams, " + - "?State, ?Media)"; + "?State, ?LastAttachPoint, ?Media)"; FillShapeCommand(cmd, prim); @@ -1320,7 +1322,16 @@ namespace OpenSim.Data.MySQL if (!(row["MediaURL"] is System.DBNull)) prim.MediaUrl = (string)row["MediaURL"]; - + + if (!(row["AttachedPosX"] is System.DBNull)) + { + prim.AttachedPos = new Vector3( + (float)(double)row["AttachedPosX"], + (float)(double)row["AttachedPosY"], + (float)(double)row["AttachedPosZ"] + ); + } + if (!(row["DynAttrs"] is System.DBNull)) prim.DynAttrs = DAMap.FromXml((string)row["DynAttrs"]); else @@ -1719,6 +1730,12 @@ namespace OpenSim.Data.MySQL cmd.Parameters.AddWithValue("LinkNumber", prim.LinkNum); cmd.Parameters.AddWithValue("MediaURL", prim.MediaUrl); + if (prim.AttachedPos != null) + { + cmd.Parameters.AddWithValue("AttachedPosX", (double)prim.AttachedPos.X); + cmd.Parameters.AddWithValue("AttachedPosY", (double)prim.AttachedPos.Y); + cmd.Parameters.AddWithValue("AttachedPosZ", (double)prim.AttachedPos.Z); + } if (prim.KeyframeMotion != null) cmd.Parameters.AddWithValue("KeyframeMotion", prim.KeyframeMotion.Serialize()); @@ -1730,7 +1747,7 @@ namespace OpenSim.Data.MySQL else cmd.Parameters.AddWithValue("Vehicle", String.Empty); - if (prim.DynAttrs.Count > 0) + if (prim.DynAttrs.CountNamespaces > 0) cmd.Parameters.AddWithValue("DynAttrs", prim.DynAttrs.ToXml()); else cmd.Parameters.AddWithValue("DynAttrs", null); @@ -1932,6 +1949,7 @@ namespace OpenSim.Data.MySQL s.ExtraParams = (byte[])row["ExtraParams"]; s.State = (byte)(int)row["State"]; + s.LastAttachPoint = (byte)(int)row["LastAttachPoint"]; if (!(row["Media"] is System.DBNull)) s.Media = PrimitiveBaseShape.MediaList.FromXml((string)row["Media"]); @@ -1978,6 +1996,7 @@ namespace OpenSim.Data.MySQL cmd.Parameters.AddWithValue("Texture", s.TextureEntry); cmd.Parameters.AddWithValue("ExtraParams", s.ExtraParams); cmd.Parameters.AddWithValue("State", s.State); + cmd.Parameters.AddWithValue("LastAttachPoint", s.LastAttachPoint); cmd.Parameters.AddWithValue("Media", null == s.Media ? null : s.Media.ToXml()); } diff --git a/OpenSim/Data/MySQL/MySQLUserProfilesData.cs b/OpenSim/Data/MySQL/MySQLUserProfilesData.cs new file mode 100644 index 0000000000..8b50c5498d --- /dev/null +++ b/OpenSim/Data/MySQL/MySQLUserProfilesData.cs @@ -0,0 +1,1102 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Data; +using System.Reflection; +using OpenSim.Data; +using OpenSim.Framework; +using MySql.Data.MySqlClient; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using log4net; + +namespace OpenSim.Data.MySQL +{ + public class UserProfilesData: IProfilesData + { + static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + #region Properites + string ConnectionString + { + get; set; + } + + protected object Lock + { + get; set; + } + + protected virtual Assembly Assembly + { + get { return GetType().Assembly; } + } + + #endregion Properties + + #region class Member Functions + public UserProfilesData(string connectionString) + { + ConnectionString = connectionString; + Init(); + } + + void Init() + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + + Migration m = new Migration(dbcon, Assembly, "UserProfiles"); + m.Update(); + } + } + #endregion Member Functions + + #region Classifieds Queries + /// + /// Gets the classified records. + /// + /// + /// Array of classified records + /// + /// + /// Creator identifier. + /// + public OSDArray GetClassifiedRecords(UUID creatorId) + { + OSDArray data = new OSDArray(); + + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + string query = "SELECT classifieduuid, name FROM classifieds WHERE creatoruuid = ?Id"; + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?Id", creatorId); + using( MySqlDataReader reader = cmd.ExecuteReader(CommandBehavior.Default)) + { + if(reader.HasRows) + { + while (reader.Read()) + { + OSDMap n = new OSDMap(); + UUID Id = UUID.Zero; + + string Name = null; + try + { + UUID.TryParse(Convert.ToString( reader["classifieduuid"]), out Id); + Name = Convert.ToString(reader["name"]); + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": UserAccount exception {0}", e.Message); + } + n.Add("classifieduuid", OSD.FromUUID(Id)); + n.Add("name", OSD.FromString(Name)); + data.Add(n); + } + } + } + } + } + return data; + } + + public bool UpdateClassifiedRecord(UserClassifiedAdd ad, ref string result) + { + string query = string.Empty; + + + query += "INSERT INTO classifieds ("; + query += "`classifieduuid`,"; + query += "`creatoruuid`,"; + query += "`creationdate`,"; + query += "`expirationdate`,"; + query += "`category`,"; + query += "`name`,"; + query += "`description`,"; + query += "`parceluuid`,"; + query += "`parentestate`,"; + query += "`snapshotuuid`,"; + query += "`simname`,"; + query += "`posglobal`,"; + query += "`parcelname`,"; + query += "`classifiedflags`,"; + query += "`priceforlisting`) "; + query += "VALUES ("; + query += "?ClassifiedId,"; + query += "?CreatorId,"; + query += "?CreatedDate,"; + query += "?ExpirationDate,"; + query += "?Category,"; + query += "?Name,"; + query += "?Description,"; + query += "?ParcelId,"; + query += "?ParentEstate,"; + query += "?SnapshotId,"; + query += "?SimName,"; + query += "?GlobalPos,"; + query += "?ParcelName,"; + query += "?Flags,"; + query += "?ListingPrice ) "; + query += "ON DUPLICATE KEY UPDATE "; + query += "category=?Category, "; + query += "expirationdate=?ExpirationDate, "; + query += "name=?Name, "; + query += "description=?Description, "; + query += "parentestate=?ParentEstate, "; + query += "posglobal=?GlobalPos, "; + query += "parcelname=?ParcelName, "; + query += "classifiedflags=?Flags, "; + query += "priceforlisting=?ListingPrice, "; + query += "snapshotuuid=?SnapshotId"; + + if(string.IsNullOrEmpty(ad.ParcelName)) + ad.ParcelName = "Unknown"; + if(ad.ParcelId == null) + ad.ParcelId = UUID.Zero; + if(string.IsNullOrEmpty(ad.Description)) + ad.Description = "No Description"; + + DateTime epoch = new DateTime(1970, 1, 1); + DateTime now = DateTime.Now; + TimeSpan epochnow = now - epoch; + TimeSpan duration; + DateTime expiration; + TimeSpan epochexp; + + if(ad.Flags == 2) + { + duration = new TimeSpan(7,0,0,0); + expiration = now.Add(duration); + epochexp = expiration - epoch; + } + else + { + duration = new TimeSpan(365,0,0,0); + expiration = now.Add(duration); + epochexp = expiration - epoch; + } + ad.CreationDate = (int)epochnow.TotalSeconds; + ad.ExpirationDate = (int)epochexp.TotalSeconds; + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?ClassifiedId", ad.ClassifiedId.ToString()); + cmd.Parameters.AddWithValue("?CreatorId", ad.CreatorId.ToString()); + cmd.Parameters.AddWithValue("?CreatedDate", ad.CreationDate.ToString()); + cmd.Parameters.AddWithValue("?ExpirationDate", ad.ExpirationDate.ToString()); + cmd.Parameters.AddWithValue("?Category", ad.Category.ToString()); + cmd.Parameters.AddWithValue("?Name", ad.Name.ToString()); + cmd.Parameters.AddWithValue("?Description", ad.Description.ToString()); + cmd.Parameters.AddWithValue("?ParcelId", ad.ParcelId.ToString()); + cmd.Parameters.AddWithValue("?ParentEstate", ad.ParentEstate.ToString()); + cmd.Parameters.AddWithValue("?SnapshotId", ad.SnapshotId.ToString ()); + cmd.Parameters.AddWithValue("?SimName", ad.SimName.ToString()); + cmd.Parameters.AddWithValue("?GlobalPos", ad.GlobalPos.ToString()); + cmd.Parameters.AddWithValue("?ParcelName", ad.ParcelName.ToString()); + cmd.Parameters.AddWithValue("?Flags", ad.Flags.ToString()); + cmd.Parameters.AddWithValue("?ListingPrice", ad.Price.ToString ()); + + cmd.ExecuteNonQuery(); + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": ClassifiedesUpdate exception {0}", e.Message); + result = e.Message; + return false; + } + return true; + } + + public bool DeleteClassifiedRecord(UUID recordId) + { + string query = string.Empty; + + query += "DELETE FROM classifieds WHERE "; + query += "classifieduuid = ?ClasifiedId"; + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?ClassifiedId", recordId.ToString()); + + lock(Lock) + { + cmd.ExecuteNonQuery(); + } + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": DeleteClassifiedRecord exception {0}", e.Message); + return false; + } + return true; + } + + public bool GetClassifiedInfo(ref UserClassifiedAdd ad, ref string result) + { + string query = string.Empty; + + query += "SELECT * FROM classifieds WHERE "; + query += "classifieduuid = ?AdId"; + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?AdId", ad.ClassifiedId.ToString()); + + using (MySqlDataReader reader = cmd.ExecuteReader()) + { + if(reader.Read ()) + { + ad.CreatorId = new UUID(reader.GetGuid("creatoruuid")); + ad.ParcelId = new UUID(reader.GetGuid("parceluuid")); + ad.SnapshotId = new UUID(reader.GetGuid("snapshotuuid")); + ad.CreationDate = Convert.ToInt32(reader["creationdate"]); + ad.ExpirationDate = Convert.ToInt32(reader["expirationdate"]); + ad.ParentEstate = Convert.ToInt32(reader["parentestate"]); + ad.Flags = (byte)reader.GetUInt32("classifiedflags"); + ad.Category = reader.GetInt32("category"); + ad.Price = reader.GetInt16("priceforlisting"); + ad.Name = reader.GetString("name"); + ad.Description = reader.GetString("description"); + ad.SimName = reader.GetString("simname"); + ad.GlobalPos = reader.GetString("posglobal"); + ad.ParcelName = reader.GetString("parcelname"); + + } + } + } + dbcon.Close(); + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": GetPickInfo exception {0}", e.Message); + } + return true; + } + #endregion Classifieds Queries + + #region Picks Queries + public OSDArray GetAvatarPicks(UUID avatarId) + { + string query = string.Empty; + + query += "SELECT `pickuuid`,`name` FROM userpicks WHERE "; + query += "creatoruuid = ?Id"; + OSDArray data = new OSDArray(); + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?Id", avatarId.ToString()); + + using (MySqlDataReader reader = cmd.ExecuteReader()) + { + if(reader.HasRows) + { + while (reader.Read()) + { + OSDMap record = new OSDMap(); + + record.Add("pickuuid",OSD.FromString((string)reader["pickuuid"])); + record.Add("name",OSD.FromString((string)reader["name"])); + data.Add(record); + } + } + } + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": GetAvatarPicks exception {0}", e.Message); + } + return data; + } + + public UserProfilePick GetPickInfo(UUID avatarId, UUID pickId) + { + string query = string.Empty; + UserProfilePick pick = new UserProfilePick(); + + query += "SELECT * FROM userpicks WHERE "; + query += "creatoruuid = ?CreatorId AND "; + query += "pickuuid = ?PickId"; + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?CreatorId", avatarId.ToString()); + cmd.Parameters.AddWithValue("?PickId", pickId.ToString()); + + using (MySqlDataReader reader = cmd.ExecuteReader()) + { + if(reader.HasRows) + { + reader.Read(); + + string description = (string)reader["description"]; + + if (string.IsNullOrEmpty(description)) + description = "No description given."; + + UUID.TryParse((string)reader["pickuuid"], out pick.PickId); + UUID.TryParse((string)reader["creatoruuid"], out pick.CreatorId); + UUID.TryParse((string)reader["parceluuid"], out pick.ParcelId); + UUID.TryParse((string)reader["snapshotuuid"], out pick.SnapshotId); + pick.GlobalPos = (string)reader["posglobal"]; + bool.TryParse((string)reader["toppick"], out pick.TopPick); + bool.TryParse((string)reader["enabled"], out pick.Enabled); + pick.Name = (string)reader["name"]; + pick.Desc = description; + pick.User = (string)reader["user"]; + pick.OriginalName = (string)reader["originalname"]; + pick.SimName = (string)reader["simname"]; + pick.SortOrder = (int)reader["sortorder"]; + } + } + } + dbcon.Close(); + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": GetPickInfo exception {0}", e.Message); + } + return pick; + } + + public bool UpdatePicksRecord(UserProfilePick pick) + { + string query = string.Empty; + + query += "INSERT INTO userpicks VALUES ("; + query += "?PickId,"; + query += "?CreatorId,"; + query += "?TopPick,"; + query += "?ParcelId,"; + query += "?Name,"; + query += "?Desc,"; + query += "?SnapshotId,"; + query += "?User,"; + query += "?Original,"; + query += "?SimName,"; + query += "?GlobalPos,"; + query += "?SortOrder,"; + query += "?Enabled) "; + query += "ON DUPLICATE KEY UPDATE "; + query += "parceluuid=?ParcelId,"; + query += "name=?Name,"; + query += "description=?Desc,"; + query += "snapshotuuid=?SnapshotId,"; + query += "pickuuid=?PickId,"; + query += "posglobal=?GlobalPos"; + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?PickId", pick.PickId.ToString()); + cmd.Parameters.AddWithValue("?CreatorId", pick.CreatorId.ToString()); + cmd.Parameters.AddWithValue("?TopPick", pick.TopPick.ToString()); + cmd.Parameters.AddWithValue("?ParcelId", pick.ParcelId.ToString()); + cmd.Parameters.AddWithValue("?Name", pick.Name.ToString()); + cmd.Parameters.AddWithValue("?Desc", pick.Desc.ToString()); + cmd.Parameters.AddWithValue("?SnapshotId", pick.SnapshotId.ToString()); + cmd.Parameters.AddWithValue("?User", pick.User.ToString()); + cmd.Parameters.AddWithValue("?Original", pick.OriginalName.ToString()); + cmd.Parameters.AddWithValue("?SimName",pick.SimName.ToString()); + cmd.Parameters.AddWithValue("?GlobalPos", pick.GlobalPos); + cmd.Parameters.AddWithValue("?SortOrder", pick.SortOrder.ToString ()); + cmd.Parameters.AddWithValue("?Enabled", pick.Enabled.ToString()); + + cmd.ExecuteNonQuery(); + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": UpdateAvatarNotes exception {0}", e.Message); + return false; + } + return true; + } + + public bool DeletePicksRecord(UUID pickId) + { + string query = string.Empty; + + query += "DELETE FROM userpicks WHERE "; + query += "pickuuid = ?PickId"; + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?PickId", pickId.ToString()); + + cmd.ExecuteNonQuery(); + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": DeleteUserPickRecord exception {0}", e.Message); + return false; + } + return true; + } + #endregion Picks Queries + + #region Avatar Notes Queries + public bool GetAvatarNotes(ref UserProfileNotes notes) + { // WIP + string query = string.Empty; + + query += "SELECT `notes` FROM usernotes WHERE "; + query += "useruuid = ?Id AND "; + query += "targetuuid = ?TargetId"; + OSDArray data = new OSDArray(); + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?Id", notes.UserId.ToString()); + cmd.Parameters.AddWithValue("?TargetId", notes.TargetId.ToString()); + + using (MySqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if(reader.HasRows) + { + reader.Read(); + notes.Notes = OSD.FromString((string)reader["notes"]); + } + else + { + notes.Notes = OSD.FromString(""); + } + } + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": GetAvatarNotes exception {0}", e.Message); + } + return true; + } + + public bool UpdateAvatarNotes(ref UserProfileNotes note, ref string result) + { + string query = string.Empty; + bool remove; + + if(string.IsNullOrEmpty(note.Notes)) + { + remove = true; + query += "DELETE FROM usernotes WHERE "; + query += "useruuid=?UserId AND "; + query += "targetuuid=?TargetId"; + } + else + { + remove = false; + query += "INSERT INTO usernotes VALUES ( "; + query += "?UserId,"; + query += "?TargetId,"; + query += "?Notes )"; + query += "ON DUPLICATE KEY "; + query += "UPDATE "; + query += "notes=?Notes"; + } + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + if(!remove) + cmd.Parameters.AddWithValue("?Notes", note.Notes); + cmd.Parameters.AddWithValue("?TargetId", note.TargetId.ToString ()); + cmd.Parameters.AddWithValue("?UserId", note.UserId.ToString()); + + cmd.ExecuteNonQuery(); + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": UpdateAvatarNotes exception {0}", e.Message); + return false; + } + return true; + + } + #endregion Avatar Notes Queries + + #region Avatar Properties + public bool GetAvatarProperties(ref UserProfileProperties props, ref string result) + { + string query = string.Empty; + + query += "SELECT * FROM userprofile WHERE "; + query += "useruuid = ?Id"; + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?Id", props.UserId.ToString()); + + using (MySqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if(reader.HasRows) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": Getting data for {0}.", props.UserId); + reader.Read(); + props.WebUrl = (string)reader["profileURL"]; + UUID.TryParse((string)reader["profileImage"], out props.ImageId); + props.AboutText = (string)reader["profileAboutText"]; + UUID.TryParse((string)reader["profileFirstImage"], out props.FirstLifeImageId); + props.FirstLifeText = (string)reader["profileFirstText"]; + UUID.TryParse((string)reader["profilePartner"], out props.PartnerId); + props.WantToMask = (int)reader["profileWantToMask"]; + props.WantToText = (string)reader["profileWantToText"]; + props.SkillsMask = (int)reader["profileSkillsMask"]; + props.SkillsText = (string)reader["profileSkillsText"]; + props.Language = (string)reader["profileLanguages"]; + } + else + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": No data for {0}", props.UserId); + + props.WebUrl = string.Empty; + props.ImageId = UUID.Zero; + props.AboutText = string.Empty; + props.FirstLifeImageId = UUID.Zero; + props.FirstLifeText = string.Empty; + props.PartnerId = UUID.Zero; + props.WantToMask = 0; + props.WantToText = string.Empty; + props.SkillsMask = 0; + props.SkillsText = string.Empty; + props.Language = string.Empty; + props.PublishProfile = false; + props.PublishMature = false; + + query = "INSERT INTO userprofile ("; + query += "useruuid, "; + query += "profilePartner, "; + query += "profileAllowPublish, "; + query += "profileMaturePublish, "; + query += "profileURL, "; + query += "profileWantToMask, "; + query += "profileWantToText, "; + query += "profileSkillsMask, "; + query += "profileSkillsText, "; + query += "profileLanguages, "; + query += "profileImage, "; + query += "profileAboutText, "; + query += "profileFirstImage, "; + query += "profileFirstText) VALUES ("; + query += "?userId, "; + query += "?profilePartner, "; + query += "?profileAllowPublish, "; + query += "?profileMaturePublish, "; + query += "?profileURL, "; + query += "?profileWantToMask, "; + query += "?profileWantToText, "; + query += "?profileSkillsMask, "; + query += "?profileSkillsText, "; + query += "?profileLanguages, "; + query += "?profileImage, "; + query += "?profileAboutText, "; + query += "?profileFirstImage, "; + query += "?profileFirstText)"; + + dbcon.Close(); + dbcon.Open(); + + using (MySqlCommand put = new MySqlCommand(query, dbcon)) + { + put.Parameters.AddWithValue("?userId", props.UserId.ToString()); + put.Parameters.AddWithValue("?profilePartner", props.PartnerId.ToString()); + put.Parameters.AddWithValue("?profileAllowPublish", props.PublishProfile); + put.Parameters.AddWithValue("?profileMaturePublish", props.PublishMature); + put.Parameters.AddWithValue("?profileURL", props.WebUrl); + put.Parameters.AddWithValue("?profileWantToMask", props.WantToMask); + put.Parameters.AddWithValue("?profileWantToText", props.WantToText); + put.Parameters.AddWithValue("?profileSkillsMask", props.SkillsMask); + put.Parameters.AddWithValue("?profileSkillsText", props.SkillsText); + put.Parameters.AddWithValue("?profileLanguages", props.Language); + put.Parameters.AddWithValue("?profileImage", props.ImageId.ToString()); + put.Parameters.AddWithValue("?profileAboutText", props.AboutText); + put.Parameters.AddWithValue("?profileFirstImage", props.FirstLifeImageId.ToString()); + put.Parameters.AddWithValue("?profileFirstText", props.FirstLifeText); + + put.ExecuteNonQuery(); + } + } + } + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": Requst properties exception {0}", e.Message); + result = e.Message; + return false; + } + return true; + } + + public bool UpdateAvatarProperties(ref UserProfileProperties props, ref string result) + { + string query = string.Empty; + + query += "UPDATE userprofile SET "; + query += "profileURL=?profileURL, "; + query += "profileImage=?image, "; + query += "profileAboutText=?abouttext,"; + query += "profileFirstImage=?firstlifeimage,"; + query += "profileFirstText=?firstlifetext "; + query += "WHERE useruuid=?uuid"; + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?profileURL", props.WebUrl); + cmd.Parameters.AddWithValue("?image", props.ImageId.ToString()); + cmd.Parameters.AddWithValue("?abouttext", props.AboutText); + cmd.Parameters.AddWithValue("?firstlifeimage", props.FirstLifeImageId.ToString()); + cmd.Parameters.AddWithValue("?firstlifetext", props.FirstLifeText); + cmd.Parameters.AddWithValue("?uuid", props.UserId.ToString()); + + cmd.ExecuteNonQuery(); + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": AgentPropertiesUpdate exception {0}", e.Message); + + return false; + } + return true; + } + #endregion Avatar Properties + + #region Avatar Interests + public bool UpdateAvatarInterests(UserProfileProperties up, ref string result) + { + string query = string.Empty; + + query += "UPDATE userprofile SET "; + query += "profileWantToMask=?WantMask, "; + query += "profileWantToText=?WantText,"; + query += "profileSkillsMask=?SkillsMask,"; + query += "profileSkillsText=?SkillsText, "; + query += "profileLanguages=?Languages "; + query += "WHERE useruuid=?uuid"; + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?WantMask", up.WantToMask); + cmd.Parameters.AddWithValue("?WantText", up.WantToText); + cmd.Parameters.AddWithValue("?SkillsMask", up.SkillsMask); + cmd.Parameters.AddWithValue("?SkillsText", up.SkillsText); + cmd.Parameters.AddWithValue("?Languages", up.Language); + cmd.Parameters.AddWithValue("?uuid", up.UserId.ToString()); + + cmd.ExecuteNonQuery(); + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": AgentInterestsUpdate exception {0}", e.Message); + result = e.Message; + return false; + } + return true; + } + #endregion Avatar Interests + + public OSDArray GetUserImageAssets(UUID avatarId) + { + OSDArray data = new OSDArray(); + string query = "SELECT `snapshotuuid` FROM {0} WHERE `creatoruuid` = ?Id"; + + // Get classified image assets + + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = new MySqlCommand(string.Format (query,"`classifieds`"), dbcon)) + { + cmd.Parameters.AddWithValue("?Id", avatarId.ToString()); + + using (MySqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if(reader.HasRows) + { + while (reader.Read()) + { + data.Add(new OSDString((string)reader["snapshotuuid"].ToString ())); + } + } + } + } + + dbcon.Close(); + dbcon.Open(); + + using (MySqlCommand cmd = new MySqlCommand(string.Format (query,"`userpicks`"), dbcon)) + { + cmd.Parameters.AddWithValue("?Id", avatarId.ToString()); + + using (MySqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if(reader.HasRows) + { + while (reader.Read()) + { + data.Add(new OSDString((string)reader["snapshotuuid"].ToString ())); + } + } + } + } + + dbcon.Close(); + dbcon.Open(); + + query = "SELECT `profileImage`, `profileFirstImage` FROM `userprofile` WHERE `useruuid` = ?Id"; + + using (MySqlCommand cmd = new MySqlCommand(string.Format (query,"`userpicks`"), dbcon)) + { + cmd.Parameters.AddWithValue("?Id", avatarId.ToString()); + + using (MySqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if(reader.HasRows) + { + while (reader.Read()) + { + data.Add(new OSDString((string)reader["profileImage"].ToString ())); + data.Add(new OSDString((string)reader["profileFirstImage"].ToString ())); + } + } + } + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": GetAvatarNotes exception {0}", e.Message); + } + return data; + } + + #region User Preferences + public OSDArray GetUserPreferences(UUID avatarId) + { + string query = string.Empty; + + query += "SELECT imviaemail,visible,email FROM "; + query += "usersettings WHERE "; + query += "useruuid = ?Id"; + + OSDArray data = new OSDArray(); + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?Id", avatarId.ToString()); + + using (MySqlDataReader reader = cmd.ExecuteReader()) + { + if(reader.HasRows) + { + reader.Read(); + OSDMap record = new OSDMap(); + + record.Add("imviaemail",OSD.FromString((string)reader["imviaemail"])); + record.Add("visible",OSD.FromString((string)reader["visible"])); + record.Add("email",OSD.FromString((string)reader["email"])); + data.Add(record); + } + else + { + dbcon.Close(); + dbcon.Open(); + + query = "INSERT INTO usersettings VALUES "; + query += "(?uuid,'false','false', ?Email)"; + + using (MySqlCommand put = new MySqlCommand(query, dbcon)) + { + +// put.Parameters.AddWithValue("?Email", pref.EMail); +// put.Parameters.AddWithValue("?uuid", pref.UserId.ToString()); + + put.ExecuteNonQuery(); + } + } + } + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": Get preferences exception {0}", e.Message); + } + return data; + } + + public bool UpdateUserPreferences(bool emailIm, bool visible, UUID avatarId ) + { + string query = string.Empty; + + query += "UPDATE userpsettings SET "; + query += "imviaemail=?ImViaEmail, "; + query += "visible=?Visible,"; + query += "WHERE useruuid=?uuid"; + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?ImViaEmail", emailIm.ToString().ToLower ()); + cmd.Parameters.AddWithValue("?WantText", visible.ToString().ToLower ()); + cmd.Parameters.AddWithValue("?uuid", avatarId.ToString()); + + lock(Lock) + { + cmd.ExecuteNonQuery(); + } + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": AgentInterestsUpdate exception {0}", e.Message); + return false; + } + return true; + } + #endregion User Preferences + + #region Integration + public bool GetUserAppData(ref UserAppData props, ref string result) + { + string query = string.Empty; + + query += "SELECT * FROM `userdata` WHERE "; + query += "UserId = ?Id AND "; + query += "TagId = ?TagId"; + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?Id", props.UserId.ToString()); + cmd.Parameters.AddWithValue ("?TagId", props.TagId.ToString()); + + using (MySqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if(reader.HasRows) + { + reader.Read(); + props.DataKey = (string)reader["DataKey"]; + props.DataVal = (string)reader["DataVal"]; + } + else + { + query += "INSERT INTO userdata VALUES ( "; + query += "?UserId,"; + query += "?TagId,"; + query += "?DataKey,"; + query += "?DataVal) "; + + using (MySqlCommand put = new MySqlCommand(query, dbcon)) + { + put.Parameters.AddWithValue("?Id", props.UserId.ToString()); + put.Parameters.AddWithValue("?TagId", props.TagId.ToString()); + put.Parameters.AddWithValue("?DataKey", props.DataKey.ToString()); + put.Parameters.AddWithValue("?DataVal", props.DataVal.ToString()); + + lock(Lock) + { + put.ExecuteNonQuery(); + } + } + } + } + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": Requst application data exception {0}", e.Message); + result = e.Message; + return false; + } + return true; + } + + public bool SetUserAppData(UserAppData props, ref string result) + { + string query = string.Empty; + + query += "UPDATE userdata SET "; + query += "TagId = ?TagId, "; + query += "DataKey = ?DataKey, "; + query += "DataVal = ?DataVal WHERE "; + query += "UserId = ?UserId AND "; + query += "TagId = ?TagId"; + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?UserId", props.UserId.ToString()); + cmd.Parameters.AddWithValue("?TagId", props.TagId.ToString ()); + cmd.Parameters.AddWithValue("?DataKey", props.DataKey.ToString ()); + cmd.Parameters.AddWithValue("?DataVal", props.DataKey.ToString ()); + + lock(Lock) + { + cmd.ExecuteNonQuery(); + } + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": SetUserData exception {0}", e.Message); + return false; + } + return true; + } + #endregion Integration + } +} + diff --git a/OpenSim/Data/MySQL/MySQLXAssetData.cs b/OpenSim/Data/MySQL/MySQLXAssetData.cs index 15ac9215e2..5f1d2ee665 100644 --- a/OpenSim/Data/MySQL/MySQLXAssetData.cs +++ b/OpenSim/Data/MySQL/MySQLXAssetData.cs @@ -199,6 +199,8 @@ namespace OpenSim.Data.MySQL /// On failure : Throw an exception and attempt to reconnect to database public void StoreAsset(AssetBase asset) { +// m_log.DebugFormat("[XASSETS DB]: Storing asset {0} {1}", asset.Name, asset.ID); + lock (m_dbLock) { using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) diff --git a/OpenSim/Data/MySQL/Properties/AssemblyInfo.cs b/OpenSim/Data/MySQL/Properties/AssemblyInfo.cs index 1146d92e9d..f562300205 100644 --- a/OpenSim/Data/MySQL/Properties/AssemblyInfo.cs +++ b/OpenSim/Data/MySQL/Properties/AssemblyInfo.cs @@ -61,5 +61,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: -[assembly : AssemblyVersion("0.7.6.*")] +[assembly : AssemblyVersion("0.8.0.*")] diff --git a/OpenSim/Data/MySQL/Resources/EstateStore.migrations b/OpenSim/Data/MySQL/Resources/EstateStore.migrations index df82a2e1f8..2d1c2b50ab 100644 --- a/OpenSim/Data/MySQL/Resources/EstateStore.migrations +++ b/OpenSim/Data/MySQL/Resources/EstateStore.migrations @@ -77,5 +77,11 @@ BEGIN; ALTER TABLE estate_settings AUTO_INCREMENT = 100; COMMIT; +:VERSION 33 #--------------------- +BEGIN; +ALTER TABLE estate_settings ADD COLUMN `AllowLandmark` tinyint(4) NOT NULL default '1'; +ALTER TABLE estate_settings ADD COLUMN `AllowParcelChanges` tinyint(4) NOT NULL default '1'; +ALTER TABLE estate_settings ADD COLUMN `AllowSetHome` tinyint(4) NOT NULL default '1'; +COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/FriendsStore.migrations b/OpenSim/Data/MySQL/Resources/FriendsStore.migrations index 55d82eca73..5faf95625f 100644 --- a/OpenSim/Data/MySQL/Resources/FriendsStore.migrations +++ b/OpenSim/Data/MySQL/Resources/FriendsStore.migrations @@ -9,7 +9,7 @@ CREATE TABLE `Friends` ( `Offered` VARCHAR(32) NOT NULL DEFAULT 0, PRIMARY KEY(`PrincipalID`, `Friend`), KEY(`PrincipalID`) -); +) ENGINE=InnoDB; COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/HGTravelStore.migrations b/OpenSim/Data/MySQL/Resources/HGTravelStore.migrations new file mode 100644 index 0000000000..b4e4422871 --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/HGTravelStore.migrations @@ -0,0 +1,18 @@ +:VERSION 1 # -------------------------- + +BEGIN; + +CREATE TABLE `hg_traveling_data` ( + `SessionID` VARCHAR(36) NOT NULL, + `UserID` VARCHAR(36) NOT NULL, + `GridExternalName` VARCHAR(255) NOT NULL DEFAULT '', + `ServiceToken` VARCHAR(255) NOT NULL DEFAULT '', + `ClientIPAddress` VARCHAR(16) NOT NULL DEFAULT '', + `MyIPAddress` VARCHAR(16) NOT NULL DEFAULT '', + `TMStamp` timestamp NOT NULL, + PRIMARY KEY (`SessionID`), + KEY (`UserID`) +) ENGINE=InnoDB; + +COMMIT; + diff --git a/OpenSim/Data/MySQL/Resources/IM_Store.migrations b/OpenSim/Data/MySQL/Resources/IM_Store.migrations index 7cfcd43888..f73475ee33 100644 --- a/OpenSim/Data/MySQL/Resources/IM_Store.migrations +++ b/OpenSim/Data/MySQL/Resources/IM_Store.migrations @@ -21,4 +21,14 @@ INSERT INTO `im_offline` SELECT * from `diva_im_offline`; DROP TABLE `diva_im_offline`; DELETE FROM `migrations` WHERE name='diva_im_Store'; -COMMIT; \ No newline at end of file +COMMIT; + +:VERSION 3 # -------------------------- + +BEGIN; + +ALTER TABLE `im_offline` + ADD `FromID` char(36) NOT NULL default '' AFTER `PrincipalID`, + ADD KEY `FromID` (`FromID`); + +COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/RegionStore.migrations b/OpenSim/Data/MySQL/Resources/RegionStore.migrations index bda1b6a60d..c2e3afe7ae 100644 --- a/OpenSim/Data/MySQL/Resources/RegionStore.migrations +++ b/OpenSim/Data/MySQL/Resources/RegionStore.migrations @@ -922,3 +922,20 @@ ALTER TABLE prims ADD COLUMN `Friction` double NOT NULL default '0.6'; ALTER TABLE prims ADD COLUMN `Restitution` double NOT NULL default '0.5'; COMMIT; + +:VERSION 48 #---------------- Keyframes + +BEGIN; + +ALTER TABLE prims ADD COLUMN `KeyframeMotion` blob; + +COMMIT; + +:VERSION 49 #--------------------- Save attachment info + +BEGIN; +ALTER TABLE prims ADD COLUMN AttachedPosX double default 0; +ALTER TABLE prims ADD COLUMN AttachedPosY double default 0; +ALTER TABLE prims ADD COLUMN AttachedPosZ double default 0; +ALTER TABLE primshapes ADD COLUMN LastAttachPoint int(4) not null default '0'; +COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/UserProfiles.migrations b/OpenSim/Data/MySQL/Resources/UserProfiles.migrations new file mode 100644 index 0000000000..c29f1abf3e --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/UserProfiles.migrations @@ -0,0 +1,83 @@ +:VERSION 1 # ------------------------------- + +begin; + +CREATE TABLE IF NOT EXISTS `classifieds` ( + `classifieduuid` char(36) NOT NULL, + `creatoruuid` char(36) NOT NULL, + `creationdate` int(20) NOT NULL, + `expirationdate` int(20) NOT NULL, + `category` varchar(20) NOT NULL, + `name` varchar(255) NOT NULL, + `description` text NOT NULL, + `parceluuid` char(36) NOT NULL, + `parentestate` int(11) NOT NULL, + `snapshotuuid` char(36) NOT NULL, + `simname` varchar(255) NOT NULL, + `posglobal` varchar(255) NOT NULL, + `parcelname` varchar(255) NOT NULL, + `classifiedflags` int(8) NOT NULL, + `priceforlisting` int(5) NOT NULL, + PRIMARY KEY (`classifieduuid`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + + +CREATE TABLE IF NOT EXISTS `usernotes` ( + `useruuid` varchar(36) NOT NULL, + `targetuuid` varchar(36) NOT NULL, + `notes` text NOT NULL, + UNIQUE KEY `useruuid` (`useruuid`,`targetuuid`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + + +CREATE TABLE IF NOT EXISTS `userpicks` ( + `pickuuid` varchar(36) NOT NULL, + `creatoruuid` varchar(36) NOT NULL, + `toppick` enum('true','false') NOT NULL, + `parceluuid` varchar(36) NOT NULL, + `name` varchar(255) NOT NULL, + `description` text NOT NULL, + `snapshotuuid` varchar(36) NOT NULL, + `user` varchar(255) NOT NULL, + `originalname` varchar(255) NOT NULL, + `simname` varchar(255) NOT NULL, + `posglobal` varchar(255) NOT NULL, + `sortorder` int(2) NOT NULL, + `enabled` enum('true','false') NOT NULL, + PRIMARY KEY (`pickuuid`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + + +CREATE TABLE IF NOT EXISTS `userprofile` ( + `useruuid` varchar(36) NOT NULL, + `profilePartner` varchar(36) NOT NULL, + `profileAllowPublish` binary(1) NOT NULL, + `profileMaturePublish` binary(1) NOT NULL, + `profileURL` varchar(255) NOT NULL, + `profileWantToMask` int(3) NOT NULL, + `profileWantToText` text NOT NULL, + `profileSkillsMask` int(3) NOT NULL, + `profileSkillsText` text NOT NULL, + `profileLanguages` text NOT NULL, + `profileImage` varchar(36) NOT NULL, + `profileAboutText` text NOT NULL, + `profileFirstImage` varchar(36) NOT NULL, + `profileFirstText` text NOT NULL, + PRIMARY KEY (`useruuid`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +commit; + +:VERSION 2 # ------------------------------- + +begin; +CREATE TABLE IF NOT EXISTS `userdata` ( + `UserId` char(36) NOT NULL, + `TagId` varchar(64) NOT NULL, + `DataKey` varchar(255), + `DataVal` varchar(255), + PRIMARY KEY (`UserId`,`TagId`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +commit; + diff --git a/OpenSim/Data/Null/NullEstateData.cs b/OpenSim/Data/Null/NullEstateData.cs index d64136de8d..1df397d943 100755 --- a/OpenSim/Data/Null/NullEstateData.cs +++ b/OpenSim/Data/Null/NullEstateData.cs @@ -42,6 +42,22 @@ namespace OpenSim.Data.Null // private string m_connectionString; + private Dictionary m_knownEstates = new Dictionary(); + private EstateSettings m_estate = null; + + private EstateSettings GetEstate() + { + if (m_estate == null) + { + // This fools the initialization caller into thinking an estate was fetched (a check in OpenSimBase). + // The estate info is pretty empty so don't try banning anyone. + m_estate = new EstateSettings(); + m_estate.EstateID = 1; + m_estate.OnSave += StoreEstateSettings; + } + return m_estate; + } + protected virtual Assembly Assembly { get { return GetType().Assembly; } @@ -68,21 +84,18 @@ namespace OpenSim.Data.Null public EstateSettings LoadEstateSettings(UUID regionID, bool create) { - // This fools the initialization caller into thinking an estate was fetched (a check in OpenSimBase). - // The estate info is pretty empty so don't try banning anyone. - EstateSettings oneEstate = new EstateSettings(); - oneEstate.EstateID = 1; - return oneEstate; + return GetEstate(); } public void StoreEstateSettings(EstateSettings es) { + m_estate = es; return; } public EstateSettings LoadEstateSettings(int estateID) { - return new EstateSettings(); + return GetEstate(); } public EstateSettings CreateNewEstate() @@ -93,13 +106,14 @@ namespace OpenSim.Data.Null public List LoadEstateSettingsAll() { List allEstateSettings = new List(); - allEstateSettings.Add(new EstateSettings()); + allEstateSettings.Add(GetEstate()); return allEstateSettings; } public List GetEstatesAll() { List result = new List(); + result.Add((int)GetEstate().EstateID); return result; } diff --git a/OpenSim/Data/Null/NullRegionData.cs b/OpenSim/Data/Null/NullRegionData.cs index f707d98cd2..d28cd9933b 100644 --- a/OpenSim/Data/Null/NullRegionData.cs +++ b/OpenSim/Data/Null/NullRegionData.cs @@ -239,6 +239,11 @@ namespace OpenSim.Data.Null return Get((int)RegionFlags.DefaultRegion, scopeID); } + public List GetDefaultHypergridRegions(UUID scopeID) + { + return Get((int)RegionFlags.DefaultHGRegion, scopeID); + } + public List GetFallbackRegions(UUID scopeID, int x, int y) { List regions = Get((int)RegionFlags.FallbackRegion, scopeID); diff --git a/OpenSim/Data/Null/NullSimulationData.cs b/OpenSim/Data/Null/NullSimulationData.cs index e7e5c41e00..15824a9ebe 100644 --- a/OpenSim/Data/Null/NullSimulationData.cs +++ b/OpenSim/Data/Null/NullSimulationData.cs @@ -77,20 +77,34 @@ namespace OpenSim.Data.Null } #region Environment Settings + + private Dictionary EnvironmentSettings = new Dictionary(); + public string LoadRegionEnvironmentSettings(UUID regionUUID) { - //This connector doesn't support the Environment module yet + lock (EnvironmentSettings) + { + if (EnvironmentSettings.ContainsKey(regionUUID)) + return EnvironmentSettings[regionUUID]; + } return string.Empty; } public void StoreRegionEnvironmentSettings(UUID regionUUID, string settings) { - //This connector doesn't support the Environment module yet + lock (EnvironmentSettings) + { + EnvironmentSettings[regionUUID] = settings; + } } public void RemoveRegionEnvironmentSettings(UUID regionUUID) { - //This connector doesn't support the Environment module yet + lock (EnvironmentSettings) + { + if (EnvironmentSettings.ContainsKey(regionUUID)) + EnvironmentSettings.Remove(regionUUID); + } } #endregion diff --git a/OpenSim/Data/Null/Properties/AssemblyInfo.cs b/OpenSim/Data/Null/Properties/AssemblyInfo.cs index 1e02c31bda..28ef51ad3f 100644 --- a/OpenSim/Data/Null/Properties/AssemblyInfo.cs +++ b/OpenSim/Data/Null/Properties/AssemblyInfo.cs @@ -61,5 +61,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: -[assembly : AssemblyVersion("0.7.6.*")] +[assembly : AssemblyVersion("0.8.0.*")] diff --git a/OpenSim/Data/PGSQL/PGSQLAssetData.cs b/OpenSim/Data/PGSQL/PGSQLAssetData.cs new file mode 100644 index 0000000000..7c5c01d9bf --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLAssetData.cs @@ -0,0 +1,296 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Data; +using System.Reflection; +using System.Collections.Generic; +using OpenMetaverse; +using log4net; +using OpenSim.Framework; +using Npgsql; +using NpgsqlTypes; + +namespace OpenSim.Data.PGSQL +{ + /// + /// A PGSQL Interface for the Asset server + /// + public class PGSQLAssetData : AssetDataBase + { + private const string _migrationStore = "AssetStore"; + + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private long m_ticksToEpoch; + /// + /// Database manager + /// + private PGSQLManager m_database; + private string m_connectionString; + + protected virtual Assembly Assembly + { + get { return GetType().Assembly; } + } + + #region IPlugin Members + + override public void Dispose() { } + + /// + /// Initialises asset interface + /// + // [Obsolete("Cannot be default-initialized!")] + override public void Initialise() + { + m_log.Info("[PGSQLAssetData]: " + Name + " cannot be default-initialized!"); + throw new PluginNotInitialisedException(Name); + } + + /// + /// Initialises asset interface + /// + /// + /// a string instead of file, if someone writes the support + /// + /// connect string + override public void Initialise(string connectionString) + { + m_ticksToEpoch = new System.DateTime(1970, 1, 1).Ticks; + + m_database = new PGSQLManager(connectionString); + m_connectionString = connectionString; + + //New migration to check for DB changes + m_database.CheckMigration(_migrationStore); + } + + /// + /// Database provider version. + /// + override public string Version + { + get { return m_database.getVersion(); } + } + + /// + /// The name of this DB provider. + /// + override public string Name + { + get { return "PGSQL Asset storage engine"; } + } + + #endregion + + #region IAssetDataPlugin Members + + /// + /// Fetch Asset from m_database + /// + /// the asset UUID + /// + override public AssetBase GetAsset(UUID assetID) + { + string sql = "SELECT * FROM assets WHERE id = :id"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(m_database.CreateParameter("id", assetID)); + conn.Open(); + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + if (reader.Read()) + { + AssetBase asset = new AssetBase( + DBGuid.FromDB(reader["id"]), + (string)reader["name"], + Convert.ToSByte(reader["assetType"]), + reader["creatorid"].ToString() + ); + // Region Main + asset.Description = (string)reader["description"]; + asset.Local = Convert.ToBoolean(reader["local"]); + asset.Temporary = Convert.ToBoolean(reader["temporary"]); + asset.Flags = (AssetFlags)(Convert.ToInt32(reader["asset_flags"])); + asset.Data = (byte[])reader["data"]; + return asset; + } + return null; // throw new Exception("No rows to return"); + } + } + } + + /// + /// Create asset in m_database + /// + /// the asset + override public bool StoreAsset(AssetBase asset) + { + + string sql = + @"UPDATE assets set name = :name, description = :description, " + "\"assetType\" " + @" = :assetType, + local = :local, temporary = :temporary, creatorid = :creatorid, data = :data + WHERE id=:id; + + INSERT INTO assets + (id, name, description, " + "\"assetType\" " + @", local, + temporary, create_time, access_time, creatorid, asset_flags, data) + Select :id, :name, :description, :assetType, :local, + :temporary, :create_time, :access_time, :creatorid, :asset_flags, :data + Where not EXISTS(SELECT * FROM assets WHERE id=:id) + "; + + string assetName = asset.Name; + if (asset.Name.Length > 64) + { + assetName = asset.Name.Substring(0, 64); + m_log.WarnFormat( + "[ASSET DB]: Name '{0}' for asset {1} truncated from {2} to {3} characters on add", + asset.Name, asset.ID, asset.Name.Length, assetName.Length); + } + + string assetDescription = asset.Description; + if (asset.Description.Length > 64) + { + assetDescription = asset.Description.Substring(0, 64); + m_log.WarnFormat( + "[ASSET DB]: Description '{0}' for asset {1} truncated from {2} to {3} characters on add", + asset.Description, asset.ID, asset.Description.Length, assetDescription.Length); + } + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand command = new NpgsqlCommand(sql, conn)) + { + int now = (int)((System.DateTime.Now.Ticks - m_ticksToEpoch) / 10000000); + command.Parameters.Add(m_database.CreateParameter("id", asset.FullID)); + command.Parameters.Add(m_database.CreateParameter("name", assetName)); + command.Parameters.Add(m_database.CreateParameter("description", assetDescription)); + command.Parameters.Add(m_database.CreateParameter("assetType", asset.Type)); + command.Parameters.Add(m_database.CreateParameter("local", asset.Local)); + command.Parameters.Add(m_database.CreateParameter("temporary", asset.Temporary)); + command.Parameters.Add(m_database.CreateParameter("access_time", now)); + command.Parameters.Add(m_database.CreateParameter("create_time", now)); + command.Parameters.Add(m_database.CreateParameter("asset_flags", (int)asset.Flags)); + command.Parameters.Add(m_database.CreateParameter("creatorid", asset.Metadata.CreatorID)); + command.Parameters.Add(m_database.CreateParameter("data", asset.Data)); + conn.Open(); + try + { + command.ExecuteNonQuery(); + } + catch(Exception e) + { + m_log.Error("[ASSET DB]: Error storing item :" + e.Message + " sql "+sql); + } + } + return true; + } + + +// Commented out since currently unused - this probably should be called in GetAsset() +// private void UpdateAccessTime(AssetBase asset) +// { +// using (AutoClosingSqlCommand cmd = m_database.Query("UPDATE assets SET access_time = :access_time WHERE id=:id")) +// { +// int now = (int)((System.DateTime.Now.Ticks - m_ticksToEpoch) / 10000000); +// cmd.Parameters.AddWithValue(":id", asset.FullID.ToString()); +// cmd.Parameters.AddWithValue(":access_time", now); +// try +// { +// cmd.ExecuteNonQuery(); +// } +// catch (Exception e) +// { +// m_log.Error(e.ToString()); +// } +// } +// } + + /// + /// Check if asset exist in m_database + /// + /// + /// true if exist. + override public bool ExistsAsset(UUID uuid) + { + if (GetAsset(uuid) != null) + { + return true; + } + return false; + } + + /// + /// Returns a list of AssetMetadata objects. The list is a subset of + /// the entire data set offset by containing + /// elements. + /// + /// The number of results to discard from the total data set. + /// The number of rows the returned list should contain. + /// A list of AssetMetadata objects. + public override List FetchAssetMetadataSet(int start, int count) + { + List retList = new List(count); + string sql = @" SELECT id, name, description, " + "\"assetType\"" + @", temporary, creatorid + FROM assets + order by id + limit :stop + offset :start;"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(m_database.CreateParameter("start", start)); + cmd.Parameters.Add(m_database.CreateParameter("stop", start + count - 1)); + conn.Open(); + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + AssetMetadata metadata = new AssetMetadata(); + metadata.FullID = DBGuid.FromDB(reader["id"]); + metadata.Name = (string)reader["name"]; + metadata.Description = (string)reader["description"]; + metadata.Type = Convert.ToSByte(reader["assetType"]); + metadata.Temporary = Convert.ToBoolean(reader["temporary"]); + metadata.CreatorID = (string)reader["creatorid"]; + retList.Add(metadata); + } + } + } + + return retList; + } + + public override bool Delete(string id) + { + return false; + } + #endregion + } +} diff --git a/OpenSim/Data/PGSQL/PGSQLAuthenticationData.cs b/OpenSim/Data/PGSQL/PGSQLAuthenticationData.cs new file mode 100644 index 0000000000..d174112f0f --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLAuthenticationData.cs @@ -0,0 +1,254 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections; +using System.Collections.Generic; +using OpenMetaverse; +using OpenSim.Framework; +using System.Reflection; +using System.Text; +using System.Data; +using Npgsql; +using NpgsqlTypes; + +namespace OpenSim.Data.PGSQL +{ + public class PGSQLAuthenticationData : IAuthenticationData + { + private string m_Realm; + private List m_ColumnNames = null; + private int m_LastExpire = 0; + private string m_ConnectionString; + private PGSQLManager m_database; + + protected virtual Assembly Assembly + { + get { return GetType().Assembly; } + } + + public PGSQLAuthenticationData(string connectionString, string realm) + { + m_Realm = realm; + m_ConnectionString = connectionString; + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + { + conn.Open(); + Migration m = new Migration(conn, GetType().Assembly, "AuthStore"); + m_database = new PGSQLManager(m_ConnectionString); + m.Update(); + } + } + + public AuthenticationData Get(UUID principalID) + { + AuthenticationData ret = new AuthenticationData(); + ret.Data = new Dictionary(); + + string sql = string.Format("select * from {0} where uuid = :principalID", m_Realm); + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(m_database.CreateParameter("principalID", principalID)); + conn.Open(); + using (NpgsqlDataReader result = cmd.ExecuteReader()) + { + if (result.Read()) + { + ret.PrincipalID = principalID; + + if (m_ColumnNames == null) + { + m_ColumnNames = new List(); + + DataTable schemaTable = result.GetSchemaTable(); + foreach (DataRow row in schemaTable.Rows) + m_ColumnNames.Add(row["ColumnName"].ToString()); + } + + foreach (string s in m_ColumnNames) + { + if (s == "UUID"||s == "uuid") + continue; + + ret.Data[s] = result[s].ToString(); + } + return ret; + } + } + } + return null; + } + + public bool Store(AuthenticationData data) + { + if (data.Data.ContainsKey("UUID")) + data.Data.Remove("UUID"); + if (data.Data.ContainsKey("uuid")) + data.Data.Remove("uuid"); + + /* + Dictionary oAuth = new Dictionary(); + + foreach (KeyValuePair oDado in data.Data) + { + if (oDado.Key != oDado.Key.ToLower()) + { + oAuth.Add(oDado.Key.ToLower(), oDado.Value); + } + } + foreach (KeyValuePair oDado in data.Data) + { + if (!oAuth.ContainsKey(oDado.Key.ToLower())) { + oAuth.Add(oDado.Key.ToLower(), oDado.Value); + } + } + */ + string[] fields = new List(data.Data.Keys).ToArray(); + StringBuilder updateBuilder = new StringBuilder(); + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + updateBuilder.AppendFormat("update {0} set ", m_Realm); + + bool first = true; + foreach (string field in fields) + { + if (!first) + updateBuilder.Append(", "); + updateBuilder.AppendFormat("\"{0}\" = :{0}",field); + + first = false; + + cmd.Parameters.Add(m_database.CreateParameter("" + field, data.Data[field])); + } + + updateBuilder.Append(" where uuid = :principalID"); + + cmd.CommandText = updateBuilder.ToString(); + cmd.Connection = conn; + cmd.Parameters.Add(m_database.CreateParameter("principalID", data.PrincipalID)); + + conn.Open(); + if (cmd.ExecuteNonQuery() < 1) + { + StringBuilder insertBuilder = new StringBuilder(); + + insertBuilder.AppendFormat("insert into {0} (uuid, \"", m_Realm); + insertBuilder.Append(String.Join("\", \"", fields)); + insertBuilder.Append("\") values (:principalID, :"); + insertBuilder.Append(String.Join(", :", fields)); + insertBuilder.Append(")"); + + cmd.CommandText = insertBuilder.ToString(); + + if (cmd.ExecuteNonQuery() < 1) + { + return false; + } + } + } + return true; + } + + public bool SetDataItem(UUID principalID, string item, string value) + { + string sql = string.Format("update {0} set {1} = :{1} where uuid = :UUID", m_Realm, item); + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(m_database.CreateParameter("" + item, value)); + conn.Open(); + if (cmd.ExecuteNonQuery() > 0) + return true; + } + return false; + } + + public bool SetToken(UUID principalID, string token, int lifetime) + { + if (System.Environment.TickCount - m_LastExpire > 30000) + DoExpire(); + + string sql = "insert into tokens (uuid, token, validity) values (:principalID, :token, :lifetime)"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(m_database.CreateParameter("principalID", principalID)); + cmd.Parameters.Add(m_database.CreateParameter("token", token)); + cmd.Parameters.Add(m_database.CreateParameter("lifetime", DateTime.Now.AddMinutes(lifetime))); + conn.Open(); + + if (cmd.ExecuteNonQuery() > 0) + { + return true; + } + } + return false; + } + + public bool CheckToken(UUID principalID, string token, int lifetime) + { + if (System.Environment.TickCount - m_LastExpire > 30000) + DoExpire(); + + DateTime validDate = DateTime.Now.AddMinutes(lifetime); + string sql = "update tokens set validity = :validDate where uuid = :principalID and token = :token and validity > (CURRENT_DATE + CURRENT_TIME)"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(m_database.CreateParameter("principalID", principalID)); + cmd.Parameters.Add(m_database.CreateParameter("token", token)); + cmd.Parameters.Add(m_database.CreateParameter("validDate", validDate)); + conn.Open(); + + if (cmd.ExecuteNonQuery() > 0) + { + return true; + } + } + return false; + } + + private void DoExpire() + { + DateTime currentDateTime = DateTime.Now; + string sql = "delete from tokens where validity < :currentDateTime"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + conn.Open(); + cmd.Parameters.Add(m_database.CreateParameter("currentDateTime", currentDateTime)); + cmd.ExecuteNonQuery(); + } + m_LastExpire = System.Environment.TickCount; + } + } +} diff --git a/OpenSim/Data/PGSQL/PGSQLAvatarData.cs b/OpenSim/Data/PGSQL/PGSQLAvatarData.cs new file mode 100644 index 0000000000..d9c49052d4 --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLAvatarData.cs @@ -0,0 +1,72 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Threading; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; +using Npgsql; +using NpgsqlTypes; + + +namespace OpenSim.Data.PGSQL +{ + /// + /// A PGSQL Interface for Avatar Storage + /// + public class PGSQLAvatarData : PGSQLGenericTableHandler, + IAvatarData + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public PGSQLAvatarData(string connectionString, string realm) : + base(connectionString, realm, "Avatar") + { + } + + public bool Delete(UUID principalID, string name) + { + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + + cmd.CommandText = String.Format("DELETE FROM {0} where \"PrincipalID\" = :PrincipalID and \"Name\" = :Name", m_Realm); + cmd.Parameters.Add(m_database.CreateParameter("PrincipalID", principalID)); + cmd.Parameters.Add(m_database.CreateParameter("Name", name)); + cmd.Connection = conn; + conn.Open(); + if (cmd.ExecuteNonQuery() > 0) + return true; + + return false; + } + } + } +} diff --git a/OpenSim/Data/PGSQL/PGSQLEstateData.cs b/OpenSim/Data/PGSQL/PGSQLEstateData.cs new file mode 100644 index 0000000000..5ad0eaabf1 --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLEstateData.cs @@ -0,0 +1,602 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Reflection; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using System.Data; +using Npgsql; +using NpgsqlTypes; + +namespace OpenSim.Data.PGSQL +{ + public class PGSQLEstateStore : IEstateDataStore + { + private const string _migrationStore = "EstateStore"; + + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private PGSQLManager _Database; + private string m_connectionString; + private FieldInfo[] _Fields; + private Dictionary _FieldMap = new Dictionary(); + + #region Public methods + + public PGSQLEstateStore() + { + } + + public PGSQLEstateStore(string connectionString) + { + Initialise(connectionString); + } + + protected virtual Assembly Assembly + { + get { return GetType().Assembly; } + } + + /// + /// Initialises the estatedata class. + /// + /// connectionString. + public void Initialise(string connectionString) + { + if (!string.IsNullOrEmpty(connectionString)) + { + m_connectionString = connectionString; + _Database = new PGSQLManager(connectionString); + } + + //Migration settings + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + { + conn.Open(); + Migration m = new Migration(conn, GetType().Assembly, "EstateStore"); + m.Update(); + } + + //Interesting way to get parameters! Maybe implement that also with other types + Type t = typeof(EstateSettings); + _Fields = t.GetFields(BindingFlags.NonPublic | + BindingFlags.Instance | + BindingFlags.DeclaredOnly); + + foreach (FieldInfo f in _Fields) + { + if (f.Name.Substring(0, 2) == "m_") + _FieldMap[f.Name.Substring(2)] = f; + } + } + + /// + /// Loads the estate settings. + /// + /// region ID. + /// + public EstateSettings LoadEstateSettings(UUID regionID, bool create) + { + EstateSettings es = new EstateSettings(); + + string sql = "select estate_settings.\"" + String.Join("\",estate_settings.\"", FieldList) + + "\" from estate_map left join estate_settings on estate_map.\"EstateID\" = estate_settings.\"EstateID\" " + + " where estate_settings.\"EstateID\" is not null and \"RegionID\" = :RegionID"; + + bool insertEstate = false; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("RegionID", regionID)); + conn.Open(); + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + if (reader.Read()) + { + foreach (string name in FieldList) + { + FieldInfo f = _FieldMap[name]; + object v = reader[name]; + if (f.FieldType == typeof(bool)) + { + f.SetValue(es, v); + } + else if (f.FieldType == typeof(UUID)) + { + UUID estUUID = UUID.Zero; + + UUID.TryParse(v.ToString(), out estUUID); + + f.SetValue(es, estUUID); + } + else if (f.FieldType == typeof(string)) + { + f.SetValue(es, v.ToString()); + } + else if (f.FieldType == typeof(UInt32)) + { + f.SetValue(es, Convert.ToUInt32(v)); + } + else if (f.FieldType == typeof(Single)) + { + f.SetValue(es, Convert.ToSingle(v)); + } + else + f.SetValue(es, v); + } + } + else + { + insertEstate = true; + } + } + } + + if (insertEstate && create) + { + DoCreate(es); + LinkRegion(regionID, (int)es.EstateID); + } + + LoadBanList(es); + + es.EstateManagers = LoadUUIDList(es.EstateID, "estate_managers"); + es.EstateAccess = LoadUUIDList(es.EstateID, "estate_users"); + es.EstateGroups = LoadUUIDList(es.EstateID, "estate_groups"); + + //Set event + es.OnSave += StoreEstateSettings; + return es; + } + + public EstateSettings CreateNewEstate() + { + EstateSettings es = new EstateSettings(); + es.OnSave += StoreEstateSettings; + + DoCreate(es); + + LoadBanList(es); + + es.EstateManagers = LoadUUIDList(es.EstateID, "estate_managers"); + es.EstateAccess = LoadUUIDList(es.EstateID, "estate_users"); + es.EstateGroups = LoadUUIDList(es.EstateID, "estate_groups"); + + return es; + } + + private void DoCreate(EstateSettings es) + { + List names = new List(FieldList); + + names.Remove("EstateID"); + + string sql = string.Format("insert into estate_settings (\"{0}\") values ( :{1} )", String.Join("\",\"", names.ToArray()), String.Join(", :", names.ToArray())); + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand insertCommand = new NpgsqlCommand(sql, conn)) + { + insertCommand.CommandText = sql; + + foreach (string name in names) + { + insertCommand.Parameters.Add(_Database.CreateParameter("" + name, _FieldMap[name].GetValue(es))); + } + //NpgsqlParameter idParameter = new NpgsqlParameter("ID", SqlDbType.Int); + //idParameter.Direction = ParameterDirection.Output; + //insertCommand.Parameters.Add(idParameter); + conn.Open(); + + es.EstateID = 100; + + if (insertCommand.ExecuteNonQuery() > 0) + { + insertCommand.CommandText = "Select cast(lastval() as int) as ID ;"; + + using (NpgsqlDataReader result = insertCommand.ExecuteReader()) + { + if (result.Read()) + { + es.EstateID = (uint)result.GetInt32(0); + } + } + } + + } + + //TODO check if this is needed?? + es.Save(); + } + + /// + /// Stores the estate settings. + /// + /// estate settings + public void StoreEstateSettings(EstateSettings es) + { + List names = new List(FieldList); + + names.Remove("EstateID"); + + string sql = string.Format("UPDATE estate_settings SET "); + foreach (string name in names) + { + sql += "\"" + name + "\" = :" + name + ", "; + } + sql = sql.Remove(sql.LastIndexOf(",")); + sql += " WHERE \"EstateID\" = :EstateID"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + foreach (string name in names) + { + cmd.Parameters.Add(_Database.CreateParameter("" + name, _FieldMap[name].GetValue(es))); + } + + cmd.Parameters.Add(_Database.CreateParameter("EstateID", es.EstateID)); + conn.Open(); + cmd.ExecuteNonQuery(); + } + + SaveBanList(es); + SaveUUIDList(es.EstateID, "estate_managers", es.EstateManagers); + SaveUUIDList(es.EstateID, "estate_users", es.EstateAccess); + SaveUUIDList(es.EstateID, "estate_groups", es.EstateGroups); + } + + #endregion + + #region Private methods + + private string[] FieldList + { + get { return new List(_FieldMap.Keys).ToArray(); } + } + + private void LoadBanList(EstateSettings es) + { + es.ClearBans(); + + string sql = "select \"bannedUUID\" from estateban where \"EstateID\" = :EstateID"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + NpgsqlParameter idParameter = new NpgsqlParameter("EstateID", DbType.Int32); + idParameter.Value = es.EstateID; + cmd.Parameters.Add(idParameter); + conn.Open(); + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + EstateBan eb = new EstateBan(); + + eb.BannedUserID = new UUID((Guid)reader["bannedUUID"]); //uuid; + eb.BannedHostAddress = "0.0.0.0"; + eb.BannedHostIPMask = "0.0.0.0"; + es.AddBan(eb); + } + } + } + } + + private UUID[] LoadUUIDList(uint estateID, string table) + { + List uuids = new List(); + + string sql = string.Format("select uuid from {0} where \"EstateID\" = :EstateID", table); + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("EstateID", estateID)); + conn.Open(); + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + uuids.Add(new UUID((Guid)reader["uuid"])); //uuid); + } + } + } + + return uuids.ToArray(); + } + + private void SaveBanList(EstateSettings es) + { + //Delete first + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + { + conn.Open(); + using (NpgsqlCommand cmd = conn.CreateCommand()) + { + cmd.CommandText = "delete from estateban where \"EstateID\" = :EstateID"; + cmd.Parameters.AddWithValue("EstateID", (int)es.EstateID); + cmd.ExecuteNonQuery(); + + //Insert after + cmd.CommandText = "insert into estateban (\"EstateID\", \"bannedUUID\",\"bannedIp\", \"bannedIpHostMask\", \"bannedNameMask\") values ( :EstateID, :bannedUUID, '','','' )"; + cmd.Parameters.AddWithValue("bannedUUID", Guid.Empty); + foreach (EstateBan b in es.EstateBans) + { + cmd.Parameters["bannedUUID"].Value = b.BannedUserID.Guid; + cmd.ExecuteNonQuery(); + } + } + } + } + + private void SaveUUIDList(uint estateID, string table, UUID[] data) + { + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + { + conn.Open(); + using (NpgsqlCommand cmd = conn.CreateCommand()) + { + cmd.Parameters.AddWithValue("EstateID", (int)estateID); + cmd.CommandText = string.Format("delete from {0} where \"EstateID\" = :EstateID", table); + cmd.ExecuteNonQuery(); + + cmd.CommandText = string.Format("insert into {0} (\"EstateID\", uuid) values ( :EstateID, :uuid )", table); + cmd.Parameters.AddWithValue("uuid", Guid.Empty); + foreach (UUID uuid in data) + { + cmd.Parameters["uuid"].Value = uuid.Guid; //.ToString(); //TODO check if this works + cmd.ExecuteNonQuery(); + } + } + } + } + + public EstateSettings LoadEstateSettings(int estateID) + { + EstateSettings es = new EstateSettings(); + string sql = "select estate_settings.\"" + String.Join("\",estate_settings.\"", FieldList) + "\" from estate_settings where \"EstateID\" = :EstateID"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + { + conn.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.AddWithValue("EstateID", (int)estateID); + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + if (reader.Read()) + { + foreach (string name in FieldList) + { + FieldInfo f = _FieldMap[name]; + object v = reader[name]; + if (f.FieldType == typeof(bool)) + { + f.SetValue(es, Convert.ToInt32(v) != 0); + } + else if (f.FieldType == typeof(UUID)) + { + f.SetValue(es, new UUID((Guid)v)); // uuid); + } + else if (f.FieldType == typeof(string)) + { + f.SetValue(es, v.ToString()); + } + else if (f.FieldType == typeof(UInt32)) + { + f.SetValue(es, Convert.ToUInt32(v)); + } + else if (f.FieldType == typeof(Single)) + { + f.SetValue(es, Convert.ToSingle(v)); + } + else + f.SetValue(es, v); + } + } + + } + } + } + LoadBanList(es); + + es.EstateManagers = LoadUUIDList(es.EstateID, "estate_managers"); + es.EstateAccess = LoadUUIDList(es.EstateID, "estate_users"); + es.EstateGroups = LoadUUIDList(es.EstateID, "estate_groups"); + + //Set event + es.OnSave += StoreEstateSettings; + return es; + + } + + public List LoadEstateSettingsAll() + { + List allEstateSettings = new List(); + + List allEstateIds = GetEstatesAll(); + + foreach (int estateId in allEstateIds) + allEstateSettings.Add(LoadEstateSettings(estateId)); + + return allEstateSettings; + } + + public List GetEstates(string search) + { + List result = new List(); + string sql = "select \"EstateID\" from estate_settings where lower(\"EstateName\") = lower(:EstateName)"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + { + conn.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.AddWithValue("EstateName", search); + + using (IDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + result.Add(Convert.ToInt32(reader["EstateID"])); + } + reader.Close(); + } + } + } + + return result; + } + + public List GetEstatesAll() + { + List result = new List(); + string sql = "select \"EstateID\" from estate_settings"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + { + conn.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + using (IDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + result.Add(Convert.ToInt32(reader["EstateID"])); + } + reader.Close(); + } + } + } + + return result; + } + + public List GetEstatesByOwner(UUID ownerID) + { + List result = new List(); + string sql = "select \"estateID\" from estate_settings where \"EstateOwner\" = :EstateOwner"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + { + conn.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.AddWithValue("EstateOwner", ownerID); + + using (IDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + result.Add(Convert.ToInt32(reader["EstateID"])); + } + reader.Close(); + } + } + } + + return result; + } + + public bool LinkRegion(UUID regionID, int estateID) + { + string deleteSQL = "delete from estate_map where \"RegionID\" = :RegionID"; + string insertSQL = "insert into estate_map values (:RegionID, :EstateID)"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + { + conn.Open(); + + NpgsqlTransaction transaction = conn.BeginTransaction(); + + try + { + using (NpgsqlCommand cmd = new NpgsqlCommand(deleteSQL, conn)) + { + cmd.Transaction = transaction; + cmd.Parameters.AddWithValue("RegionID", regionID.Guid); + + cmd.ExecuteNonQuery(); + } + + using (NpgsqlCommand cmd = new NpgsqlCommand(insertSQL, conn)) + { + cmd.Transaction = transaction; + cmd.Parameters.AddWithValue("RegionID", regionID.Guid); + cmd.Parameters.AddWithValue("EstateID", estateID); + + int ret = cmd.ExecuteNonQuery(); + + if (ret != 0) + transaction.Commit(); + else + transaction.Rollback(); + + return (ret != 0); + } + } + catch (Exception ex) + { + m_log.Error("[REGION DB]: LinkRegion failed: " + ex.Message); + transaction.Rollback(); + } + } + return false; + } + + public List GetRegions(int estateID) + { + List result = new List(); + string sql = "select \"RegionID\" from estate_map where \"EstateID\" = :EstateID"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + { + conn.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.AddWithValue("EstateID", estateID); + + using (IDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + result.Add(DBGuid.FromDB(reader["RegionID"])); + } + reader.Close(); + } + } + } + + return result; + } + + public bool DeleteEstate(int estateID) + { + // TODO: Implementation! + return false; + } + #endregion + } +} diff --git a/OpenSim/Data/PGSQL/PGSQLFramework.cs b/OpenSim/Data/PGSQL/PGSQLFramework.cs new file mode 100644 index 0000000000..1028e4ea86 --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLFramework.cs @@ -0,0 +1,111 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Data; +using System.Reflection; +using OpenMetaverse; +using OpenSim.Framework; +using Npgsql; + +namespace OpenSim.Data.PGSQL +{ + /// + /// A database interface class to a user profile storage system + /// + public class PGSqlFramework + { + private static readonly log4net.ILog m_log = + log4net.LogManager.GetLogger( + System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + protected string m_connectionString; + protected object m_dbLock = new object(); + + protected PGSqlFramework(string connectionString) + { + m_connectionString = connectionString; + InitializeMonoSecurity(); + } + + public void InitializeMonoSecurity() + { + if (!Util.IsPlatformMono) + { + + if (AppDomain.CurrentDomain.GetData("MonoSecurityPostgresAdded") == null) + { + AppDomain.CurrentDomain.SetData("MonoSecurityPostgresAdded", "true"); + + AppDomain currentDomain = AppDomain.CurrentDomain; + currentDomain.AssemblyResolve += new ResolveEventHandler(ResolveEventHandlerMonoSec); + } + } + } + + private System.Reflection.Assembly ResolveEventHandlerMonoSec(object sender, ResolveEventArgs args) + { + Assembly MyAssembly = null; + + if (args.Name.Substring(0, args.Name.IndexOf(",")) == "Mono.Security") + { + MyAssembly = Assembly.LoadFrom("lib/NET/Mono.Security.dll"); + } + + //Return the loaded assembly. + return MyAssembly; + } + ////////////////////////////////////////////////////////////// + // + // All non queries are funneled through one connection + // to increase performance a little + // + protected int ExecuteNonQuery(NpgsqlCommand cmd) + { + lock (m_dbLock) + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(m_connectionString)) + { + dbcon.Open(); + cmd.Connection = dbcon; + + try + { + return cmd.ExecuteNonQuery(); + } + catch (Exception e) + { + m_log.Error(e.Message, e); + return 0; + } + } + } + } + } +} diff --git a/OpenSim/Data/PGSQL/PGSQLFriendsData.cs b/OpenSim/Data/PGSQL/PGSQLFriendsData.cs new file mode 100644 index 0000000000..4c1ee02e41 --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLFriendsData.cs @@ -0,0 +1,116 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Data; +using OpenMetaverse; +using OpenSim.Framework; +using System.Reflection; +using System.Text; +using Npgsql; + +namespace OpenSim.Data.PGSQL +{ + public class PGSQLFriendsData : PGSQLGenericTableHandler, IFriendsData + { + public PGSQLFriendsData(string connectionString, string realm) + : base(connectionString, realm, "FriendsStore") + { + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + { + conn.Open(); + Migration m = new Migration(conn, GetType().Assembly, "FriendsStore"); + m.Update(); + } + } + + + public bool Delete(string principalID, string friend) + { + UUID princUUID = UUID.Zero; + + bool ret = UUID.TryParse(principalID, out princUUID); + + if (ret) + return Delete(princUUID, friend); + else + return false; + } + + public bool Delete(UUID principalID, string friend) + { + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + cmd.CommandText = String.Format("delete from {0} where \"PrincipalID\" = :PrincipalID and \"Friend\" = :Friend", m_Realm); + cmd.Parameters.Add(m_database.CreateParameter("PrincipalID", principalID.ToString())); + cmd.Parameters.Add(m_database.CreateParameter("Friend", friend)); + cmd.Connection = conn; + conn.Open(); + cmd.ExecuteNonQuery(); + + return true; + } + } + + public FriendsData[] GetFriends(string principalID) + { + UUID princUUID = UUID.Zero; + + bool ret = UUID.TryParse(principalID, out princUUID); + + if (ret) + return GetFriends(princUUID); + else + return new FriendsData[0]; + } + + public FriendsData[] GetFriends(UUID principalID) + { + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + + cmd.CommandText = String.Format("select a.*,case when b.\"Flags\" is null then '-1' else b.\"Flags\" end as \"TheirFlags\" from {0} as a " + + " left join {0} as b on a.\"PrincipalID\" = b.\"Friend\" and a.\"Friend\" = b.\"PrincipalID\" " + + " where a.\"PrincipalID\" = :PrincipalID", m_Realm); + cmd.Parameters.Add(m_database.CreateParameter("PrincipalID", principalID.ToString())); + cmd.Connection = conn; + conn.Open(); + return DoQuery(cmd); + } + } + + public FriendsData[] GetFriends(Guid principalID) + { + return GetFriends(principalID); + } + + } +} diff --git a/OpenSim/Data/PGSQL/PGSQLGenericTableHandler.cs b/OpenSim/Data/PGSQL/PGSQLGenericTableHandler.cs new file mode 100644 index 0000000000..2151568a01 --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLGenericTableHandler.cs @@ -0,0 +1,519 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Data; +using System.Reflection; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using System.Text; +using Npgsql; + +namespace OpenSim.Data.PGSQL +{ + public class PGSQLGenericTableHandler : PGSqlFramework where T : class, new() + { + private static readonly ILog m_log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected string m_ConnectionString; + protected PGSQLManager m_database; //used for parameter type translation + protected Dictionary m_Fields = + new Dictionary(); + + protected Dictionary m_FieldTypes = new Dictionary(); + + protected List m_ColumnNames = null; + protected string m_Realm; + protected FieldInfo m_DataField = null; + + protected virtual Assembly Assembly + { + get { return GetType().Assembly; } + } + + public PGSQLGenericTableHandler(string connectionString, + string realm, string storeName) + : base(connectionString) + { + m_Realm = realm; + + m_ConnectionString = connectionString; + + if (storeName != String.Empty) + { + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + { + conn.Open(); + Migration m = new Migration(conn, GetType().Assembly, storeName); + m.Update(); + } + + } + m_database = new PGSQLManager(m_ConnectionString); + + Type t = typeof(T); + FieldInfo[] fields = t.GetFields(BindingFlags.Public | + BindingFlags.Instance | + BindingFlags.DeclaredOnly); + + LoadFieldTypes(); + + if (fields.Length == 0) + return; + + foreach (FieldInfo f in fields) + { + if (f.Name != "Data") + m_Fields[f.Name] = f; + else + m_DataField = f; + } + + } + + private void LoadFieldTypes() + { + m_FieldTypes = new Dictionary(); + + string query = string.Format(@"select column_name,data_type + from INFORMATION_SCHEMA.COLUMNS + where table_name = lower('{0}'); + + ", m_Realm); + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(query, conn)) + { + conn.Open(); + using (NpgsqlDataReader rdr = cmd.ExecuteReader()) + { + while (rdr.Read()) + { + // query produces 0 to many rows of single column, so always add the first item in each row + m_FieldTypes.Add((string)rdr[0], (string)rdr[1]); + } + } + } + } + + private void CheckColumnNames(NpgsqlDataReader reader) + { + if (m_ColumnNames != null) + return; + + m_ColumnNames = new List(); + + DataTable schemaTable = reader.GetSchemaTable(); + + foreach (DataRow row in schemaTable.Rows) + { + if (row["ColumnName"] != null && + (!m_Fields.ContainsKey(row["ColumnName"].ToString()))) + m_ColumnNames.Add(row["ColumnName"].ToString()); + + } + } + + // TODO GET CONSTRAINTS FROM POSTGRESQL + private List GetConstraints() + { + List constraints = new List(); + string query = string.Format(@"SELECT kcu.column_name + FROM information_schema.table_constraints tc + LEFT JOIN information_schema.key_column_usage kcu + ON tc.constraint_catalog = kcu.constraint_catalog + AND tc.constraint_schema = kcu.constraint_schema + AND tc.constraint_name = kcu.constraint_name + + LEFT JOIN information_schema.referential_constraints rc + ON tc.constraint_catalog = rc.constraint_catalog + AND tc.constraint_schema = rc.constraint_schema + AND tc.constraint_name = rc.constraint_name + + LEFT JOIN information_schema.constraint_column_usage ccu + ON rc.unique_constraint_catalog = ccu.constraint_catalog + AND rc.unique_constraint_schema = ccu.constraint_schema + AND rc.unique_constraint_name = ccu.constraint_name + + where tc.table_name = lower('{0}') + and lower(tc.constraint_type) in ('primary key') + and kcu.column_name is not null + ;", m_Realm); + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(query, conn)) + { + conn.Open(); + using (NpgsqlDataReader rdr = cmd.ExecuteReader()) + { + while (rdr.Read()) + { + // query produces 0 to many rows of single column, so always add the first item in each row + constraints.Add((string)rdr[0]); + } + } + return constraints; + } + } + + public virtual T[] Get(string field, string key) + { + return Get(new string[] { field }, new string[] { key }); + } + + public virtual T[] Get(string[] fields, string[] keys) + { + if (fields.Length != keys.Length) + return new T[0]; + + List terms = new List(); + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + + for (int i = 0; i < fields.Length; i++) + { + if ( m_FieldTypes.ContainsKey(fields[i]) ) + cmd.Parameters.Add(m_database.CreateParameter(fields[i], keys[i], m_FieldTypes[fields[i]])); + else + cmd.Parameters.Add(m_database.CreateParameter(fields[i], keys[i])); + + terms.Add(" \"" + fields[i] + "\" = :" + fields[i]); + } + + string where = String.Join(" AND ", terms.ToArray()); + + string query = String.Format("SELECT * FROM {0} WHERE {1}", + m_Realm, where); + + cmd.Connection = conn; + cmd.CommandText = query; + conn.Open(); + return DoQuery(cmd); + } + } + + protected T[] DoQuery(NpgsqlCommand cmd) + { + List result = new List(); + if (cmd.Connection == null) + { + cmd.Connection = new NpgsqlConnection(m_connectionString); + } + if (cmd.Connection.State == ConnectionState.Closed) + { + cmd.Connection.Open(); + } + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + if (reader == null) + return new T[0]; + + CheckColumnNames(reader); + + while (reader.Read()) + { + T row = new T(); + + foreach (string name in m_Fields.Keys) + { + if (m_Fields[name].GetValue(row) is bool) + { + int v = Convert.ToInt32(reader[name]); + m_Fields[name].SetValue(row, v != 0 ? true : false); + } + else if (m_Fields[name].GetValue(row) is UUID) + { + UUID uuid = UUID.Zero; + + UUID.TryParse(reader[name].ToString(), out uuid); + m_Fields[name].SetValue(row, uuid); + } + else if (m_Fields[name].GetValue(row) is int) + { + int v = Convert.ToInt32(reader[name]); + m_Fields[name].SetValue(row, v); + } + else + { + m_Fields[name].SetValue(row, reader[name]); + } + } + + if (m_DataField != null) + { + Dictionary data = + new Dictionary(); + + foreach (string col in m_ColumnNames) + { + data[col] = reader[col].ToString(); + + if (data[col] == null) + data[col] = String.Empty; + } + + m_DataField.SetValue(row, data); + } + + result.Add(row); + } + return result.ToArray(); + } + } + + public virtual T[] Get(string where) + { + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + + string query = String.Format("SELECT * FROM {0} WHERE {1}", + m_Realm, where); + cmd.Connection = conn; + cmd.CommandText = query; + + //m_log.WarnFormat("[PGSQLGenericTable]: SELECT {0} WHERE {1}", m_Realm, where); + + conn.Open(); + return DoQuery(cmd); + } + } + + public virtual bool Store(T row) + { + List constraintFields = GetConstraints(); + List> constraints = new List>(); + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + + StringBuilder query = new StringBuilder(); + List names = new List(); + List values = new List(); + + foreach (FieldInfo fi in m_Fields.Values) + { + names.Add(fi.Name); + values.Add(":" + fi.Name); + // Temporarily return more information about what field is unexpectedly null for + // http://opensimulator.org/mantis/view.php?id=5403. This might be due to a bug in the + // InventoryTransferModule or we may be required to substitute a DBNull here. + if (fi.GetValue(row) == null) + throw new NullReferenceException( + string.Format( + "[PGSQL GENERIC TABLE HANDLER]: Trying to store field {0} for {1} which is unexpectedly null", + fi.Name, row)); + + if (constraintFields.Count > 0 && constraintFields.Contains(fi.Name)) + { + constraints.Add(new KeyValuePair(fi.Name, fi.GetValue(row).ToString() )); + } + if (m_FieldTypes.ContainsKey(fi.Name)) + cmd.Parameters.Add(m_database.CreateParameter(fi.Name, fi.GetValue(row), m_FieldTypes[fi.Name])); + else + cmd.Parameters.Add(m_database.CreateParameter(fi.Name, fi.GetValue(row))); + } + + if (m_DataField != null) + { + Dictionary data = + (Dictionary)m_DataField.GetValue(row); + + foreach (KeyValuePair kvp in data) + { + if (constraintFields.Count > 0 && constraintFields.Contains(kvp.Key)) + { + constraints.Add(new KeyValuePair(kvp.Key, kvp.Key)); + } + names.Add(kvp.Key); + values.Add(":" + kvp.Key); + + if (m_FieldTypes.ContainsKey(kvp.Key)) + cmd.Parameters.Add(m_database.CreateParameter("" + kvp.Key, kvp.Value, m_FieldTypes[kvp.Key])); + else + cmd.Parameters.Add(m_database.CreateParameter("" + kvp.Key, kvp.Value)); + } + + } + + query.AppendFormat("UPDATE {0} SET ", m_Realm); + int i = 0; + for (i = 0; i < names.Count - 1; i++) + { + query.AppendFormat("\"{0}\" = {1}, ", names[i], values[i]); + } + query.AppendFormat("\"{0}\" = {1} ", names[i], values[i]); + if (constraints.Count > 0) + { + List terms = new List(); + for (int j = 0; j < constraints.Count; j++) + { + terms.Add(String.Format(" \"{0}\" = :{0}", constraints[j].Key)); + } + string where = String.Join(" AND ", terms.ToArray()); + query.AppendFormat(" WHERE {0} ", where); + + } + cmd.Connection = conn; + cmd.CommandText = query.ToString(); + + conn.Open(); + if (cmd.ExecuteNonQuery() > 0) + { + //m_log.WarnFormat("[PGSQLGenericTable]: Updating {0}", m_Realm); + return true; + } + else + { + // assume record has not yet been inserted + + query = new StringBuilder(); + query.AppendFormat("INSERT INTO {0} (\"", m_Realm); + query.Append(String.Join("\",\"", names.ToArray())); + query.Append("\") values (" + String.Join(",", values.ToArray()) + ")"); + cmd.Connection = conn; + cmd.CommandText = query.ToString(); + + // m_log.WarnFormat("[PGSQLGenericTable]: Inserting into {0} sql {1}", m_Realm, cmd.CommandText); + + if (conn.State != ConnectionState.Open) + conn.Open(); + if (cmd.ExecuteNonQuery() > 0) + return true; + } + + return false; + } + } + + public virtual bool Delete(string field, string key) + { + return Delete(new string[] { field }, new string[] { key }); + } + + public virtual bool Delete(string[] fields, string[] keys) + { + if (fields.Length != keys.Length) + return false; + + List terms = new List(); + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + for (int i = 0; i < fields.Length; i++) + { + if (m_FieldTypes.ContainsKey(fields[i])) + cmd.Parameters.Add(m_database.CreateParameter(fields[i], keys[i], m_FieldTypes[fields[i]])); + else + cmd.Parameters.Add(m_database.CreateParameter(fields[i], keys[i])); + + terms.Add(" \"" + fields[i] + "\" = :" + fields[i]); + } + + string where = String.Join(" AND ", terms.ToArray()); + + string query = String.Format("DELETE FROM {0} WHERE {1}", m_Realm, where); + + cmd.Connection = conn; + cmd.CommandText = query; + conn.Open(); + + if (cmd.ExecuteNonQuery() > 0) + { + //m_log.Warn("[PGSQLGenericTable]: " + deleteCommand); + return true; + } + return false; + } + } + public long GetCount(string field, string key) + { + return GetCount(new string[] { field }, new string[] { key }); + } + + public long GetCount(string[] fields, string[] keys) + { + if (fields.Length != keys.Length) + return 0; + + List terms = new List(); + + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + for (int i = 0; i < fields.Length; i++) + { + cmd.Parameters.AddWithValue(fields[i], keys[i]); + terms.Add("\"" + fields[i] + "\" = :" + fields[i]); + } + + string where = String.Join(" and ", terms.ToArray()); + + string query = String.Format("select count(*) from {0} where {1}", + m_Realm, where); + + cmd.CommandText = query; + + Object result = DoQueryScalar(cmd); + + return Convert.ToInt64(result); + } + } + + public long GetCount(string where) + { + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + string query = String.Format("select count(*) from {0} where {1}", + m_Realm, where); + + cmd.CommandText = query; + + object result = DoQueryScalar(cmd); + + return Convert.ToInt64(result); + } + } + + public object DoQueryScalar(NpgsqlCommand cmd) + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(m_ConnectionString)) + { + dbcon.Open(); + cmd.Connection = dbcon; + + return cmd.ExecuteScalar(); + } + } + } +} diff --git a/OpenSim/Data/PGSQL/PGSQLGridUserData.cs b/OpenSim/Data/PGSQL/PGSQLGridUserData.cs new file mode 100644 index 0000000000..89319f3a58 --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLGridUserData.cs @@ -0,0 +1,68 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Data; +using System.Reflection; +using System.Threading; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; + +namespace OpenSim.Data.PGSQL +{ + /// + /// A PGSQL Interface for Avatar Storage + /// + public class PGSQLGridUserData : PGSQLGenericTableHandler, + IGridUserData + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public PGSQLGridUserData(string connectionString, string realm) : + base(connectionString, realm, "GridUserStore") + { + } + + public new GridUserData Get(string userID) + { + GridUserData[] ret = Get("UserID", userID); + + if (ret.Length == 0) + return null; + + return ret[0]; + } + + public GridUserData[] GetAll(string userID) + { + return base.Get(String.Format("\"UserID\" LIKE '{0}%'", userID)); + } + + } +} diff --git a/OpenSim/Data/PGSQL/PGSQLGroupsData.cs b/OpenSim/Data/PGSQL/PGSQLGroupsData.cs new file mode 100644 index 0000000000..ed75b638a9 --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLGroupsData.cs @@ -0,0 +1,481 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using OpenSim.Framework; +using OpenMetaverse; +using Npgsql; + +namespace OpenSim.Data.PGSQL +{ + public class PGSQLGroupsData : IGroupsData + { + private PGSqlGroupsGroupsHandler m_Groups; + private PGSqlGroupsMembershipHandler m_Membership; + private PGSqlGroupsRolesHandler m_Roles; + private PGSqlGroupsRoleMembershipHandler m_RoleMembership; + private PGSqlGroupsInvitesHandler m_Invites; + private PGSqlGroupsNoticesHandler m_Notices; + private PGSqlGroupsPrincipalsHandler m_Principals; + + public PGSQLGroupsData(string connectionString, string realm) + { + m_Groups = new PGSqlGroupsGroupsHandler(connectionString, realm + "_groups", realm + "_Store"); + m_Membership = new PGSqlGroupsMembershipHandler(connectionString, realm + "_membership"); + m_Roles = new PGSqlGroupsRolesHandler(connectionString, realm + "_roles"); + m_RoleMembership = new PGSqlGroupsRoleMembershipHandler(connectionString, realm + "_rolemembership"); + m_Invites = new PGSqlGroupsInvitesHandler(connectionString, realm + "_invites"); + m_Notices = new PGSqlGroupsNoticesHandler(connectionString, realm + "_notices"); + m_Principals = new PGSqlGroupsPrincipalsHandler(connectionString, realm + "_principals"); + } + + #region groups table + public bool StoreGroup(GroupData data) + { + return m_Groups.Store(data); + } + + public GroupData RetrieveGroup(UUID groupID) + { + GroupData[] groups = m_Groups.Get("GroupID", groupID.ToString()); + if (groups.Length > 0) + return groups[0]; + + return null; + } + + public GroupData RetrieveGroup(string name) + { + GroupData[] groups = m_Groups.Get("Name", name); + if (groups.Length > 0) + return groups[0]; + + return null; + } + + public GroupData[] RetrieveGroups(string pattern) + { + if (string.IsNullOrEmpty(pattern)) // True for where clause + pattern = " true ORDER BY lower(\"Name\") LIMIT 100"; + else + pattern = string.Format(" lower(\"Name\") LIKE lower('%{0}%') ORDER BY lower(\"Name\") LIMIT 100", pattern); + + return m_Groups.Get(pattern); + } + + public bool DeleteGroup(UUID groupID) + { + return m_Groups.Delete("GroupID", groupID.ToString()); + } + + public int GroupsCount() + { + return (int)m_Groups.GetCount(" \"Location\" = \"\""); + } + + #endregion + + #region membership table + public MembershipData[] RetrieveMembers(UUID groupID) + { + return m_Membership.Get("GroupID", groupID.ToString()); + } + + public MembershipData RetrieveMember(UUID groupID, string pricipalID) + { + MembershipData[] m = m_Membership.Get(new string[] { "GroupID", "PrincipalID" }, + new string[] { groupID.ToString(), pricipalID }); + if (m != null && m.Length > 0) + return m[0]; + + return null; + } + + public MembershipData[] RetrieveMemberships(string pricipalID) + { + return m_Membership.Get("PrincipalID", pricipalID.ToString()); + } + + public bool StoreMember(MembershipData data) + { + return m_Membership.Store(data); + } + + public bool DeleteMember(UUID groupID, string pricipalID) + { + return m_Membership.Delete(new string[] { "GroupID", "PrincipalID" }, + new string[] { groupID.ToString(), pricipalID }); + } + + public int MemberCount(UUID groupID) + { + return (int)m_Membership.GetCount("GroupID", groupID.ToString()); + } + #endregion + + #region roles table + public bool StoreRole(RoleData data) + { + return m_Roles.Store(data); + } + + public RoleData RetrieveRole(UUID groupID, UUID roleID) + { + RoleData[] data = m_Roles.Get(new string[] { "GroupID", "RoleID" }, + new string[] { groupID.ToString(), roleID.ToString() }); + + if (data != null && data.Length > 0) + return data[0]; + + return null; + } + + public RoleData[] RetrieveRoles(UUID groupID) + { + //return m_Roles.RetrieveRoles(groupID); + return m_Roles.Get("GroupID", groupID.ToString()); + } + + public bool DeleteRole(UUID groupID, UUID roleID) + { + return m_Roles.Delete(new string[] { "GroupID", "RoleID" }, + new string[] { groupID.ToString(), roleID.ToString() }); + } + + public int RoleCount(UUID groupID) + { + return (int)m_Roles.GetCount("GroupID", groupID.ToString()); + } + + + #endregion + + #region rolememberhip table + public RoleMembershipData[] RetrieveRolesMembers(UUID groupID) + { + RoleMembershipData[] data = m_RoleMembership.Get("GroupID", groupID.ToString()); + + return data; + } + + public RoleMembershipData[] RetrieveRoleMembers(UUID groupID, UUID roleID) + { + RoleMembershipData[] data = m_RoleMembership.Get(new string[] { "GroupID", "RoleID" }, + new string[] { groupID.ToString(), roleID.ToString() }); + + return data; + } + + public RoleMembershipData[] RetrieveMemberRoles(UUID groupID, string principalID) + { + RoleMembershipData[] data = m_RoleMembership.Get(new string[] { "GroupID", "PrincipalID" }, + new string[] { groupID.ToString(), principalID.ToString() }); + + return data; + } + + public RoleMembershipData RetrieveRoleMember(UUID groupID, UUID roleID, string principalID) + { + RoleMembershipData[] data = m_RoleMembership.Get(new string[] { "GroupID", "RoleID", "PrincipalID" }, + new string[] { groupID.ToString(), roleID.ToString(), principalID.ToString() }); + + if (data != null && data.Length > 0) + return data[0]; + + return null; + } + + public int RoleMemberCount(UUID groupID, UUID roleID) + { + return (int)m_RoleMembership.GetCount(new string[] { "GroupID", "RoleID" }, + new string[] { groupID.ToString(), roleID.ToString() }); + } + + public bool StoreRoleMember(RoleMembershipData data) + { + return m_RoleMembership.Store(data); + } + + public bool DeleteRoleMember(RoleMembershipData data) + { + return m_RoleMembership.Delete(new string[] { "GroupID", "RoleID", "PrincipalID"}, + new string[] { data.GroupID.ToString(), data.RoleID.ToString(), data.PrincipalID }); + } + + public bool DeleteMemberAllRoles(UUID groupID, string principalID) + { + return m_RoleMembership.Delete(new string[] { "GroupID", "PrincipalID" }, + new string[] { groupID.ToString(), principalID }); + } + + #endregion + + #region principals table + public bool StorePrincipal(PrincipalData data) + { + return m_Principals.Store(data); + } + + public PrincipalData RetrievePrincipal(string principalID) + { + PrincipalData[] p = m_Principals.Get("PrincipalID", principalID); + if (p != null && p.Length > 0) + return p[0]; + + return null; + } + + public bool DeletePrincipal(string principalID) + { + return m_Principals.Delete("PrincipalID", principalID); + } + #endregion + + #region invites table + + public bool StoreInvitation(InvitationData data) + { + return m_Invites.Store(data); + } + + public InvitationData RetrieveInvitation(UUID inviteID) + { + InvitationData[] invites = m_Invites.Get("InviteID", inviteID.ToString()); + + if (invites != null && invites.Length > 0) + return invites[0]; + + return null; + } + + public InvitationData RetrieveInvitation(UUID groupID, string principalID) + { + InvitationData[] invites = m_Invites.Get(new string[] { "GroupID", "PrincipalID" }, + new string[] { groupID.ToString(), principalID }); + + if (invites != null && invites.Length > 0) + return invites[0]; + + return null; + } + + public bool DeleteInvite(UUID inviteID) + { + return m_Invites.Delete("InviteID", inviteID.ToString()); + } + + public void DeleteOldInvites() + { + m_Invites.DeleteOld(); + } + + #endregion + + #region notices table + + public bool StoreNotice(NoticeData data) + { + return m_Notices.Store(data); + } + + public NoticeData RetrieveNotice(UUID noticeID) + { + NoticeData[] notices = m_Notices.Get("NoticeID", noticeID.ToString()); + + if (notices != null && notices.Length > 0) + return notices[0]; + + return null; + } + + public NoticeData[] RetrieveNotices(UUID groupID) + { + NoticeData[] notices = m_Notices.Get("GroupID", groupID.ToString()); + + return notices; + } + + public bool DeleteNotice(UUID noticeID) + { + return m_Notices.Delete("NoticeID", noticeID.ToString()); + } + + public void DeleteOldNotices() + { + m_Notices.DeleteOld(); + } + + #endregion + + #region combinations + public MembershipData RetrievePrincipalGroupMembership(string principalID, UUID groupID) + { + // TODO + return null; + } + public MembershipData[] RetrievePrincipalGroupMemberships(string principalID) + { + // TODO + return null; + } + + #endregion + } + + public class PGSqlGroupsGroupsHandler : PGSQLGenericTableHandler + { + protected override Assembly Assembly + { + // WARNING! Moving migrations to this assembly!!! + get { return GetType().Assembly; } + } + + public PGSqlGroupsGroupsHandler(string connectionString, string realm, string store) + : base(connectionString, realm, store) + { + } + + } + + public class PGSqlGroupsMembershipHandler : PGSQLGenericTableHandler + { + protected override Assembly Assembly + { + // WARNING! Moving migrations to this assembly!!! + get { return GetType().Assembly; } + } + + public PGSqlGroupsMembershipHandler(string connectionString, string realm) + : base(connectionString, realm, string.Empty) + { + } + + } + + public class PGSqlGroupsRolesHandler : PGSQLGenericTableHandler + { + protected override Assembly Assembly + { + // WARNING! Moving migrations to this assembly!!! + get { return GetType().Assembly; } + } + + public PGSqlGroupsRolesHandler(string connectionString, string realm) + : base(connectionString, realm, string.Empty) + { + } + + } + + public class PGSqlGroupsRoleMembershipHandler : PGSQLGenericTableHandler + { + protected override Assembly Assembly + { + // WARNING! Moving migrations to this assembly!!! + get { return GetType().Assembly; } + } + + public PGSqlGroupsRoleMembershipHandler(string connectionString, string realm) + : base(connectionString, realm, string.Empty) + { + } + + } + + public class PGSqlGroupsInvitesHandler : PGSQLGenericTableHandler + { + protected override Assembly Assembly + { + // WARNING! Moving migrations to this assembly!!! + get { return GetType().Assembly; } + } + + public PGSqlGroupsInvitesHandler(string connectionString, string realm) + : base(connectionString, realm, string.Empty) + { + } + + public void DeleteOld() + { + uint now = (uint)Util.UnixTimeSinceEpoch(); + + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + cmd.CommandText = String.Format("delete from {0} where \"TMStamp\" < :tstamp", m_Realm); + cmd.Parameters.AddWithValue("tstamp", now - 14 * 24 * 60 * 60); // > 2 weeks old + + ExecuteNonQuery(cmd); + } + + } + } + + public class PGSqlGroupsNoticesHandler : PGSQLGenericTableHandler + { + protected override Assembly Assembly + { + // WARNING! Moving migrations to this assembly!!! + get { return GetType().Assembly; } + } + + public PGSqlGroupsNoticesHandler(string connectionString, string realm) + : base(connectionString, realm, string.Empty) + { + } + + public void DeleteOld() + { + uint now = (uint)Util.UnixTimeSinceEpoch(); + + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + cmd.CommandText = String.Format("delete from {0} where \"TMStamp\" < :tstamp", m_Realm); + cmd.Parameters.AddWithValue("tstamp", now - 14 * 24 * 60 * 60); // > 2 weeks old + + ExecuteNonQuery(cmd); + } + + } + } + + public class PGSqlGroupsPrincipalsHandler : PGSQLGenericTableHandler + { + protected override Assembly Assembly + { + // WARNING! Moving migrations to this assembly!!! + get { return GetType().Assembly; } + } + + public PGSqlGroupsPrincipalsHandler(string connectionString, string realm) + : base(connectionString, realm, string.Empty) + { + } + } +} diff --git a/OpenSim/Data/PGSQL/PGSQLHGTravelData.cs b/OpenSim/Data/PGSQL/PGSQLHGTravelData.cs new file mode 100644 index 0000000000..c71b15f474 --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLHGTravelData.cs @@ -0,0 +1,80 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Data; +using System.Reflection; +using System.Threading; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; +using Npgsql; + +namespace OpenSim.Data.PGSQL +{ + /// + /// A PGSQL Interface for user grid data + /// + public class PGSQLHGTravelData : PGSQLGenericTableHandler, IHGTravelingData + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public PGSQLHGTravelData(string connectionString, string realm) : base(connectionString, realm, "HGTravelStore") { } + + public HGTravelingData Get(UUID sessionID) + { + HGTravelingData[] ret = Get("SessionID", sessionID.ToString()); + + if (ret.Length == 0) + return null; + + return ret[0]; + } + + public HGTravelingData[] GetSessions(UUID userID) + { + return base.Get("UserID", userID.ToString()); + } + + public bool Delete(UUID sessionID) + { + return Delete("SessionID", sessionID.ToString()); + } + + public void DeleteOld() + { + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + cmd.CommandText = String.Format(@"delete from {0} where ""TMStamp"" < CURRENT_DATE - INTERVAL '2 day'", m_Realm); + + ExecuteNonQuery(cmd); + } + + } + } +} diff --git a/OpenSim/Data/PGSQL/PGSQLInventoryData.cs b/OpenSim/Data/PGSQL/PGSQLInventoryData.cs new file mode 100644 index 0000000000..c9994332fa --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLInventoryData.cs @@ -0,0 +1,831 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Data; +using System.Reflection; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; +using Npgsql; + +namespace OpenSim.Data.PGSQL +{ + /// + /// A PGSQL interface for the inventory server + /// + public class PGSQLInventoryData : IInventoryDataPlugin + { + private const string _migrationStore = "InventoryStore"; + + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// The database manager + /// + private PGSQLManager database; + private string m_connectionString; + + #region IPlugin members + + [Obsolete("Cannot be default-initialized!")] + public void Initialise() + { + m_log.Info("[PGSQLInventoryData]: " + Name + " cannot be default-initialized!"); + throw new PluginNotInitialisedException(Name); + } + + /// + /// Loads and initialises the PGSQL inventory storage interface + /// + /// connect string + /// use PGSQL_connection.ini + public void Initialise(string connectionString) + { + m_connectionString = connectionString; + database = new PGSQLManager(connectionString); + + //New migrations check of store + database.CheckMigration(_migrationStore); + } + + /// + /// The name of this DB provider + /// + /// A string containing the name of the DB provider + public string Name + { + get { return "PGSQL Inventory Data Interface"; } + } + + /// + /// Closes this DB provider + /// + public void Dispose() + { + database = null; + } + + /// + /// Returns the version of this DB provider + /// + /// A string containing the DB provider + public string Version + { + get { return database.getVersion(); } + } + + #endregion + + #region Folder methods + + /// + /// Returns a list of the root folders within a users inventory + /// + /// The user whos inventory is to be searched + /// A list of folder objects + public List getUserRootFolders(UUID user) + { + if (user == UUID.Zero) + return new List(); + + return getInventoryFolders(UUID.Zero, user); + } + + /// + /// see InventoryItemBase.getUserRootFolder + /// + /// the User UUID + /// + public InventoryFolderBase getUserRootFolder(UUID user) + { + List items = getUserRootFolders(user); + + InventoryFolderBase rootFolder = null; + + // There should only ever be one root folder for a user. However, if there's more + // than one we'll simply use the first one rather than failing. It would be even + // nicer to print some message to this effect, but this feels like it's too low a + // to put such a message out, and it's too minor right now to spare the time to + // suitably refactor. + if (items.Count > 0) + { + rootFolder = items[0]; + } + + return rootFolder; + } + + /// + /// Returns a list of folders in a users inventory contained within the specified folder + /// + /// The folder to search + /// A list of inventory folders + public List getInventoryFolders(UUID parentID) + { + return getInventoryFolders(parentID, UUID.Zero); + } + + /// + /// Returns a specified inventory folder + /// + /// The folder to return + /// A folder class + public InventoryFolderBase getInventoryFolder(UUID folderID) + { + string sql = "SELECT * FROM inventoryfolders WHERE \"folderID\" = :folderID"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(database.CreateParameter("folderID", folderID)); + conn.Open(); + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + if (reader.Read()) + { + return readInventoryFolder(reader); + } + } + } + m_log.InfoFormat("[INVENTORY DB] : Found no inventory folder with ID : {0}", folderID); + return null; + } + + /// + /// Returns all child folders in the hierarchy from the parent folder and down. + /// Does not return the parent folder itself. + /// + /// The folder to get subfolders for + /// A list of inventory folders + public List getFolderHierarchy(UUID parentID) + { + //Note maybe change this to use a Dataset that loading in all folders of a user and then go throw it that way. + //Note this is changed so it opens only one connection to the database and not everytime it wants to get data. + + /* NOTE: the implementation below is very inefficient (makes a separate request to get subfolders for + * every found folder, recursively). Inventory code for other DBs has been already rewritten to get ALL + * inventory for a specific user at once. + * + * Meanwhile, one little thing is corrected: getFolderHierarchy(UUID.Zero) doesn't make sense and should never + * be used, so check for that and return an empty list. + */ + + List folders = new List(); + + if (parentID == UUID.Zero) + return folders; + + string sql = "SELECT * FROM inventoryfolders WHERE \"parentFolderID\" = :parentID"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(database.CreateParameter("parentID", parentID)); + conn.Open(); + folders.AddRange(getInventoryFolders(cmd)); + + List tempFolders = new List(); + + foreach (InventoryFolderBase folderBase in folders) + { + tempFolders.AddRange(getFolderHierarchy(folderBase.ID, cmd)); + } + if (tempFolders.Count > 0) + { + folders.AddRange(tempFolders); + } + } + return folders; + } + + /// + /// Creates a new inventory folder + /// + /// Folder to create + public void addInventoryFolder(InventoryFolderBase folder) + { + string sql = "INSERT INTO inventoryfolders (\"folderID\", \"agentID\", \"parentFolderID\", \"folderName\", type, version) " + + " VALUES (:folderID, :agentID, :parentFolderID, :folderName, :type, :version);"; + + string folderName = folder.Name; + if (folderName.Length > 64) + { + folderName = folderName.Substring(0, 64); + m_log.Warn("[INVENTORY DB]: Name field truncated from " + folder.Name.Length.ToString() + " to " + folderName.Length + " characters on add"); + } + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(database.CreateParameter("folderID", folder.ID)); + cmd.Parameters.Add(database.CreateParameter("agentID", folder.Owner)); + cmd.Parameters.Add(database.CreateParameter("parentFolderID", folder.ParentID)); + cmd.Parameters.Add(database.CreateParameter("folderName", folderName)); + cmd.Parameters.Add(database.CreateParameter("type", folder.Type)); + cmd.Parameters.Add(database.CreateParameter("version", folder.Version)); + conn.Open(); + try + { + cmd.ExecuteNonQuery(); + } + catch (Exception e) + { + m_log.ErrorFormat("[INVENTORY DB]: Error : {0}", e.Message); + } + } + } + + /// + /// Updates an inventory folder + /// + /// Folder to update + public void updateInventoryFolder(InventoryFolderBase folder) + { + string sql = @"UPDATE inventoryfolders SET ""agentID"" = :agentID, + ""parentFolderID"" = :parentFolderID, + ""folderName"" = :folderName, + type = :type, + version = :version + WHERE folderID = :folderID"; + + string folderName = folder.Name; + if (folderName.Length > 64) + { + folderName = folderName.Substring(0, 64); + m_log.Warn("[INVENTORY DB]: Name field truncated from " + folder.Name.Length.ToString() + " to " + folderName.Length + " characters on update"); + } + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(database.CreateParameter("folderID", folder.ID)); + cmd.Parameters.Add(database.CreateParameter("agentID", folder.Owner)); + cmd.Parameters.Add(database.CreateParameter("parentFolderID", folder.ParentID)); + cmd.Parameters.Add(database.CreateParameter("folderName", folderName)); + cmd.Parameters.Add(database.CreateParameter("type", folder.Type)); + cmd.Parameters.Add(database.CreateParameter("version", folder.Version)); + conn.Open(); + try + { + cmd.ExecuteNonQuery(); + } + catch (Exception e) + { + m_log.ErrorFormat("[INVENTORY DB]: Error : {0}", e.Message); + } + } + } + + /// + /// Updates an inventory folder + /// + /// Folder to update + public void moveInventoryFolder(InventoryFolderBase folder) + { + string sql = @"UPDATE inventoryfolders SET ""parentFolderID"" = :parentFolderID WHERE ""folderID"" = :folderID"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(database.CreateParameter("parentFolderID", folder.ParentID)); + cmd.Parameters.Add(database.CreateParameter("folderID", folder.ID)); + conn.Open(); + try + { + cmd.ExecuteNonQuery(); + } + catch (Exception e) + { + m_log.ErrorFormat("[INVENTORY DB]: Error : {0}", e.Message); + } + } + } + + /// + /// Delete an inventory folder + /// + /// Id of folder to delete + public void deleteInventoryFolder(UUID folderID) + { + string sql = @"SELECT * FROM inventoryfolders WHERE ""parentFolderID"" = :parentID"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + List subFolders; + cmd.Parameters.Add(database.CreateParameter("parentID", UUID.Zero)); + conn.Open(); + subFolders = getFolderHierarchy(folderID, cmd); + + + //Delete all sub-folders + foreach (InventoryFolderBase f in subFolders) + { + DeleteOneFolder(f.ID, conn); + DeleteItemsInFolder(f.ID, conn); + } + + //Delete the actual row + DeleteOneFolder(folderID, conn); + DeleteItemsInFolder(folderID, conn); + } + } + + #endregion + + #region Item Methods + + /// + /// Returns a list of items in a specified folder + /// + /// The folder to search + /// A list containing inventory items + public List getInventoryInFolder(UUID folderID) + { + string sql = @"SELECT * FROM inventoryitems WHERE ""parentFolderID"" = :parentFolderID"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(database.CreateParameter("parentFolderID", folderID)); + conn.Open(); + List items = new List(); + + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + items.Add(readInventoryItem(reader)); + } + } + return items; + } + } + + /// + /// Returns a specified inventory item + /// + /// The item ID + /// An inventory item + public InventoryItemBase getInventoryItem(UUID itemID) + { + string sql = @"SELECT * FROM inventoryitems WHERE ""inventoryID"" = :inventoryID"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(database.CreateParameter("inventoryID", itemID)); + conn.Open(); + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + if (reader.Read()) + { + return readInventoryItem(reader); + } + } + } + + m_log.InfoFormat("[INVENTORY DB]: Found no inventory item with ID : {0}", itemID); + return null; + } + + /// + /// Adds a specified item to the database + /// + /// The inventory item + public void addInventoryItem(InventoryItemBase item) + { + if (getInventoryItem(item.ID) != null) + { + updateInventoryItem(item); + return; + } + + string sql = @"INSERT INTO inventoryitems + (""inventoryID"", ""assetID"", ""assetType"", ""parentFolderID"", ""avatarID"", ""inventoryName"", + ""inventoryDescription"", ""inventoryNextPermissions"", ""inventoryCurrentPermissions"", + ""invType"", ""creatorID"", ""inventoryBasePermissions"", ""inventoryEveryOnePermissions"", ""inventoryGroupPermissions"", + ""salePrice"", ""SaleType"", ""creationDate"", ""groupID"", ""groupOwned"", flags) + VALUES + (:inventoryID, :assetID, :assetType, :parentFolderID, :avatarID, :inventoryName, :inventoryDescription, + :inventoryNextPermissions, :inventoryCurrentPermissions, :invType, :creatorID, + :inventoryBasePermissions, :inventoryEveryOnePermissions, :inventoryGroupPermissions, :SalePrice, :SaleType, + :creationDate, :groupID, :groupOwned, :flags)"; + + string itemName = item.Name; + if (item.Name.Length > 64) + { + itemName = item.Name.Substring(0, 64); + m_log.Warn("[INVENTORY DB]: Name field truncated from " + item.Name.Length.ToString() + " to " + itemName.Length.ToString() + " characters"); + } + + string itemDesc = item.Description; + if (item.Description.Length > 128) + { + itemDesc = item.Description.Substring(0, 128); + m_log.Warn("[INVENTORY DB]: Description field truncated from " + item.Description.Length.ToString() + " to " + itemDesc.Length.ToString() + " characters"); + } + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand command = new NpgsqlCommand(sql, conn)) + { + command.Parameters.Add(database.CreateParameter("inventoryID", item.ID)); + command.Parameters.Add(database.CreateParameter("assetID", item.AssetID)); + command.Parameters.Add(database.CreateParameter("assetType", item.AssetType)); + command.Parameters.Add(database.CreateParameter("parentFolderID", item.Folder)); + command.Parameters.Add(database.CreateParameter("avatarID", item.Owner)); + command.Parameters.Add(database.CreateParameter("inventoryName", itemName)); + command.Parameters.Add(database.CreateParameter("inventoryDescription", itemDesc)); + command.Parameters.Add(database.CreateParameter("inventoryNextPermissions", item.NextPermissions)); + command.Parameters.Add(database.CreateParameter("inventoryCurrentPermissions", item.CurrentPermissions)); + command.Parameters.Add(database.CreateParameter("invType", item.InvType)); + command.Parameters.Add(database.CreateParameter("creatorID", item.CreatorId)); + command.Parameters.Add(database.CreateParameter("inventoryBasePermissions", item.BasePermissions)); + command.Parameters.Add(database.CreateParameter("inventoryEveryOnePermissions", item.EveryOnePermissions)); + command.Parameters.Add(database.CreateParameter("inventoryGroupPermissions", item.GroupPermissions)); + command.Parameters.Add(database.CreateParameter("SalePrice", item.SalePrice)); + command.Parameters.Add(database.CreateParameter("SaleType", item.SaleType)); + command.Parameters.Add(database.CreateParameter("creationDate", item.CreationDate)); + command.Parameters.Add(database.CreateParameter("groupID", item.GroupID)); + command.Parameters.Add(database.CreateParameter("groupOwned", item.GroupOwned)); + command.Parameters.Add(database.CreateParameter("flags", item.Flags)); + conn.Open(); + try + { + command.ExecuteNonQuery(); + } + catch (Exception e) + { + m_log.Error("[INVENTORY DB]: Error inserting item :" + e.Message); + } + } + + sql = @"UPDATE inventoryfolders SET version = version + 1 WHERE ""folderID"" = @folderID"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand command = new NpgsqlCommand(sql, conn)) + { + command.Parameters.Add(database.CreateParameter("folderID", item.Folder.ToString())); + conn.Open(); + try + { + command.ExecuteNonQuery(); + } + catch (Exception e) + { + m_log.Error("[INVENTORY DB] Error updating inventory folder for new item :" + e.Message); + } + } + } + + /// + /// Updates the specified inventory item + /// + /// Inventory item to update + public void updateInventoryItem(InventoryItemBase item) + { + string sql = @"UPDATE inventoryitems SET ""assetID"" = :assetID, + ""assetType"" = :assetType, + ""parentFolderID"" = :parentFolderID, + ""avatarID"" = :avatarID, + ""inventoryName"" = :inventoryName, + ""inventoryDescription"" = :inventoryDescription, + ""inventoryNextPermissions"" = :inventoryNextPermissions, + ""inventoryCurrentPermissions"" = :inventoryCurrentPermissions, + ""invType"" = :invType, + ""creatorID"" = :creatorID, + ""inventoryBasePermissions"" = :inventoryBasePermissions, + ""inventoryEveryOnePermissions"" = :inventoryEveryOnePermissions, + ""inventoryGroupPermissions"" = :inventoryGroupPermissions, + ""salePrice"" = :SalePrice, + ""saleType"" = :SaleType, + ""creationDate"" = :creationDate, + ""groupID"" = :groupID, + ""groupOwned"" = :groupOwned, + flags = :flags + WHERE ""inventoryID"" = :inventoryID"; + + string itemName = item.Name; + if (item.Name.Length > 64) + { + itemName = item.Name.Substring(0, 64); + m_log.Warn("[INVENTORY DB]: Name field truncated from " + item.Name.Length.ToString() + " to " + itemName.Length.ToString() + " characters on update"); + } + + string itemDesc = item.Description; + if (item.Description.Length > 128) + { + itemDesc = item.Description.Substring(0, 128); + m_log.Warn("[INVENTORY DB]: Description field truncated from " + item.Description.Length.ToString() + " to " + itemDesc.Length.ToString() + " characters on update"); + } + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand command = new NpgsqlCommand(sql, conn)) + { + command.Parameters.Add(database.CreateParameter("inventoryID", item.ID)); + command.Parameters.Add(database.CreateParameter("assetID", item.AssetID)); + command.Parameters.Add(database.CreateParameter("assetType", item.AssetType)); + command.Parameters.Add(database.CreateParameter("parentFolderID", item.Folder)); + command.Parameters.Add(database.CreateParameter("avatarID", item.Owner)); + command.Parameters.Add(database.CreateParameter("inventoryName", itemName)); + command.Parameters.Add(database.CreateParameter("inventoryDescription", itemDesc)); + command.Parameters.Add(database.CreateParameter("inventoryNextPermissions", item.NextPermissions)); + command.Parameters.Add(database.CreateParameter("inventoryCurrentPermissions", item.CurrentPermissions)); + command.Parameters.Add(database.CreateParameter("invType", item.InvType)); + command.Parameters.Add(database.CreateParameter("creatorID", item.CreatorId)); + command.Parameters.Add(database.CreateParameter("inventoryBasePermissions", item.BasePermissions)); + command.Parameters.Add(database.CreateParameter("inventoryEveryOnePermissions", item.EveryOnePermissions)); + command.Parameters.Add(database.CreateParameter("inventoryGroupPermissions", item.GroupPermissions)); + command.Parameters.Add(database.CreateParameter("SalePrice", item.SalePrice)); + command.Parameters.Add(database.CreateParameter("SaleType", item.SaleType)); + command.Parameters.Add(database.CreateParameter("creationDate", item.CreationDate)); + command.Parameters.Add(database.CreateParameter("groupID", item.GroupID)); + command.Parameters.Add(database.CreateParameter("groupOwned", item.GroupOwned)); + command.Parameters.Add(database.CreateParameter("flags", item.Flags)); + conn.Open(); + try + { + command.ExecuteNonQuery(); + } + catch (Exception e) + { + m_log.Error("[INVENTORY DB]: Error updating item :" + e.Message); + } + } + } + + // See IInventoryDataPlugin + + /// + /// Delete an item in inventory database + /// + /// the item UUID + public void deleteInventoryItem(UUID itemID) + { + string sql = @"DELETE FROM inventoryitems WHERE ""inventoryID""=:inventoryID"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(database.CreateParameter("inventoryID", itemID)); + try + { + conn.Open(); + cmd.ExecuteNonQuery(); + } + catch (Exception e) + { + m_log.Error("[INVENTORY DB]: Error deleting item :" + e.Message); + } + } + } + + public InventoryItemBase queryInventoryItem(UUID itemID) + { + return getInventoryItem(itemID); + } + + public InventoryFolderBase queryInventoryFolder(UUID folderID) + { + return getInventoryFolder(folderID); + } + + /// + /// Returns all activated gesture-items in the inventory of the specified avatar. + /// + /// The of the avatar + /// + /// The list of gestures (s) + /// + public List fetchActiveGestures(UUID avatarID) + { + string sql = @"SELECT * FROM inventoryitems WHERE ""avatarID"" = :uuid AND ""assetType"" = :assetType and flags = 1"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(database.CreateParameter("uuid", avatarID)); + cmd.Parameters.Add(database.CreateParameter("assetType", (int)AssetType.Gesture)); + conn.Open(); + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + List gestureList = new List(); + while (reader.Read()) + { + gestureList.Add(readInventoryItem(reader)); + } + return gestureList; + } + } + } + + #endregion + + #region Private methods + + /// + /// Delete an item in inventory database + /// + /// the item ID + /// connection to the database + private void DeleteItemsInFolder(UUID folderID, NpgsqlConnection connection) + { + using (NpgsqlCommand command = new NpgsqlCommand(@"DELETE FROM inventoryitems WHERE ""folderID""=:folderID", connection)) + { + command.Parameters.Add(database.CreateParameter("folderID", folderID)); + + try + { + command.ExecuteNonQuery(); + } + catch (Exception e) + { + m_log.Error("[INVENTORY DB] Error deleting item :" + e.Message); + } + } + } + + /// + /// Gets the folder hierarchy in a loop. + /// + /// parent ID. + /// SQL command/connection to database + /// + private static List getFolderHierarchy(UUID parentID, NpgsqlCommand command) + { + command.Parameters["parentID"].Value = parentID.Guid; //.ToString(); + + List folders = getInventoryFolders(command); + + if (folders.Count > 0) + { + List tempFolders = new List(); + + foreach (InventoryFolderBase folderBase in folders) + { + tempFolders.AddRange(getFolderHierarchy(folderBase.ID, command)); + } + + if (tempFolders.Count > 0) + { + folders.AddRange(tempFolders); + } + } + return folders; + } + + /// + /// Gets the inventory folders. + /// + /// parentID, use UUID.Zero to get root + /// user id, use UUID.Zero, if you want all folders from a parentID. + /// + private List getInventoryFolders(UUID parentID, UUID user) + { + string sql = @"SELECT * FROM inventoryfolders WHERE ""parentFolderID"" = :parentID AND ""agentID"" = :uuid"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand command = new NpgsqlCommand(sql, conn)) + { + if (user == UUID.Zero) + { + command.Parameters.Add(database.CreateParameter("uuid", "%")); + } + else + { + command.Parameters.Add(database.CreateParameter("uuid", user)); + } + command.Parameters.Add(database.CreateParameter("parentID", parentID)); + conn.Open(); + return getInventoryFolders(command); + } + } + + /// + /// Gets the inventory folders. + /// + /// SQLcommand. + /// + private static List getInventoryFolders(NpgsqlCommand command) + { + using (NpgsqlDataReader reader = command.ExecuteReader()) + { + + List items = new List(); + while (reader.Read()) + { + items.Add(readInventoryFolder(reader)); + } + return items; + } + } + + /// + /// Reads a list of inventory folders returned by a query. + /// + /// A PGSQL Data Reader + /// A List containing inventory folders + protected static InventoryFolderBase readInventoryFolder(NpgsqlDataReader reader) + { + try + { + InventoryFolderBase folder = new InventoryFolderBase(); + folder.Owner = DBGuid.FromDB(reader["agentID"]); + folder.ParentID = DBGuid.FromDB(reader["parentFolderID"]); + folder.ID = DBGuid.FromDB(reader["folderID"]); + folder.Name = (string)reader["folderName"]; + folder.Type = (short)reader["type"]; + folder.Version = Convert.ToUInt16(reader["version"]); + + return folder; + } + catch (Exception e) + { + m_log.Error("[INVENTORY DB] Error reading inventory folder :" + e.Message); + } + + return null; + } + + /// + /// Reads a one item from an SQL result + /// + /// The SQL Result + /// the item read + private static InventoryItemBase readInventoryItem(IDataRecord reader) + { + try + { + InventoryItemBase item = new InventoryItemBase(); + + item.ID = DBGuid.FromDB(reader["inventoryID"]); + item.AssetID = DBGuid.FromDB(reader["assetID"]); + item.AssetType = Convert.ToInt32(reader["assetType"].ToString()); + item.Folder = DBGuid.FromDB(reader["parentFolderID"]); + item.Owner = DBGuid.FromDB(reader["avatarID"]); + item.Name = reader["inventoryName"].ToString(); + item.Description = reader["inventoryDescription"].ToString(); + item.NextPermissions = Convert.ToUInt32(reader["inventoryNextPermissions"]); + item.CurrentPermissions = Convert.ToUInt32(reader["inventoryCurrentPermissions"]); + item.InvType = Convert.ToInt32(reader["invType"].ToString()); + item.CreatorId = reader["creatorID"].ToString(); + item.BasePermissions = Convert.ToUInt32(reader["inventoryBasePermissions"]); + item.EveryOnePermissions = Convert.ToUInt32(reader["inventoryEveryOnePermissions"]); + item.GroupPermissions = Convert.ToUInt32(reader["inventoryGroupPermissions"]); + item.SalePrice = Convert.ToInt32(reader["salePrice"]); + item.SaleType = Convert.ToByte(reader["saleType"]); + item.CreationDate = Convert.ToInt32(reader["creationDate"]); + item.GroupID = DBGuid.FromDB(reader["groupID"]); + item.GroupOwned = Convert.ToBoolean(reader["groupOwned"]); + item.Flags = Convert.ToUInt32(reader["flags"]); + + return item; + } + catch (NpgsqlException e) + { + m_log.Error("[INVENTORY DB]: Error reading inventory item :" + e.Message); + } + + return null; + } + + /// + /// Delete a folder in inventory databasae + /// + /// the folder UUID + /// connection to database + private void DeleteOneFolder(UUID folderID, NpgsqlConnection connection) + { + try + { + using (NpgsqlCommand command = new NpgsqlCommand(@"DELETE FROM inventoryfolders WHERE ""folderID""=:folderID and type=-1", connection)) + { + command.Parameters.Add(database.CreateParameter("folderID", folderID)); + + command.ExecuteNonQuery(); + } + } + catch (NpgsqlException e) + { + m_log.Error("[INVENTORY DB]: Error deleting folder :" + e.Message); + } + } + + #endregion + } +} diff --git a/OpenSim/Data/PGSQL/PGSQLManager.cs b/OpenSim/Data/PGSQL/PGSQLManager.cs new file mode 100644 index 0000000000..97f40b260b --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLManager.cs @@ -0,0 +1,350 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Data; +using System.IO; +using System.Reflection; +using OpenSim.Framework; +using log4net; +using OpenMetaverse; +using Npgsql; +using NpgsqlTypes; + +namespace OpenSim.Data.PGSQL +{ + /// + /// A management class for the MS SQL Storage Engine + /// + public class PGSQLManager + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Connection string for ADO.net + /// + private readonly string connectionString; + + /// + /// Initialize the manager and set the connectionstring + /// + /// + public PGSQLManager(string connection) + { + connectionString = connection; + InitializeMonoSecurity(); + } + + public void InitializeMonoSecurity() + { + if (!Util.IsPlatformMono) + { + if (AppDomain.CurrentDomain.GetData("MonoSecurityPostgresAdded") == null) + { + AppDomain.CurrentDomain.SetData("MonoSecurityPostgresAdded", "true"); + + AppDomain currentDomain = AppDomain.CurrentDomain; + currentDomain.AssemblyResolve += new ResolveEventHandler(ResolveEventHandlerMonoSec); + } + } + } + + private System.Reflection.Assembly ResolveEventHandlerMonoSec(object sender, ResolveEventArgs args) + { + Assembly MyAssembly = null; + + if (args.Name.Substring(0, args.Name.IndexOf(",")) == "Mono.Security") + { + MyAssembly = Assembly.LoadFrom("lib/NET/Mono.Security.dll"); + } + + //Return the loaded assembly. + return MyAssembly; + } + + /// + /// Type conversion to a SQLDbType functions + /// + /// + /// + internal NpgsqlDbType DbtypeFromType(Type type) + { + if (type == typeof(string)) + { + return NpgsqlDbType.Varchar; + } + if (type == typeof(double)) + { + return NpgsqlDbType.Double; + } + if (type == typeof(Single)) + { + return NpgsqlDbType.Double; + } + if (type == typeof(int)) + { + return NpgsqlDbType.Integer; + } + if (type == typeof(bool)) + { + return NpgsqlDbType.Boolean; + } + if (type == typeof(UUID)) + { + return NpgsqlDbType.Uuid; + } + if (type == typeof(byte)) + { + return NpgsqlDbType.Smallint; + } + if (type == typeof(sbyte)) + { + return NpgsqlDbType.Integer; + } + if (type == typeof(Byte[])) + { + return NpgsqlDbType.Bytea; + } + if (type == typeof(uint) || type == typeof(ushort)) + { + return NpgsqlDbType.Integer; + } + if (type == typeof(ulong)) + { + return NpgsqlDbType.Bigint; + } + if (type == typeof(DateTime)) + { + return NpgsqlDbType.Timestamp; + } + + return NpgsqlDbType.Varchar; + } + + internal NpgsqlDbType DbtypeFromString(Type type, string PGFieldType) + { + if (PGFieldType == "") + { + return DbtypeFromType(type); + } + + if (PGFieldType == "character varying") + { + return NpgsqlDbType.Varchar; + } + if (PGFieldType == "double precision") + { + return NpgsqlDbType.Double; + } + if (PGFieldType == "integer") + { + return NpgsqlDbType.Integer; + } + if (PGFieldType == "smallint") + { + return NpgsqlDbType.Smallint; + } + if (PGFieldType == "boolean") + { + return NpgsqlDbType.Boolean; + } + if (PGFieldType == "uuid") + { + return NpgsqlDbType.Uuid; + } + if (PGFieldType == "bytea") + { + return NpgsqlDbType.Bytea; + } + + return DbtypeFromType(type); + } + + /// + /// Creates value for parameter. + /// + /// The value. + /// + private static object CreateParameterValue(object value) + { + Type valueType = value.GetType(); + + if (valueType == typeof(UUID)) //TODO check if this works + { + return ((UUID) value).Guid; + } + if (valueType == typeof(UUID)) + { + return ((UUID)value).Guid; + } + if (valueType == typeof(bool)) + { + return (bool)value; + } + if (valueType == typeof(Byte[])) + { + return value; + } + if (valueType == typeof(int)) + { + return value; + } + return value; + } + + /// + /// Create value for parameter based on PGSQL Schema + /// + /// + /// + /// + internal static object CreateParameterValue(object value, string PGFieldType) + { + if (PGFieldType == "uuid") + { + UUID uidout; + UUID.TryParse(value.ToString(), out uidout); + return uidout; + } + if (PGFieldType == "integer") + { + int intout; + int.TryParse(value.ToString(), out intout); + return intout; + } + if (PGFieldType == "boolean") + { + return (value.ToString() == "true"); + } + if (PGFieldType == "timestamp with time zone") + { + return (DateTime)value; + } + if (PGFieldType == "timestamp without time zone") + { + return (DateTime)value; + } + return CreateParameterValue(value); + } + + /// + /// Create a parameter for a command + /// + /// Name of the parameter. + /// parameter object. + /// + internal NpgsqlParameter CreateParameter(string parameterName, object parameterObject) + { + return CreateParameter(parameterName, parameterObject, false); + } + + /// + /// Creates the parameter for a command. + /// + /// Name of the parameter. + /// parameter object. + /// if set to true parameter is a output parameter + /// + internal NpgsqlParameter CreateParameter(string parameterName, object parameterObject, bool parameterOut) + { + //Tweak so we dont always have to add : sign + if (parameterName.StartsWith(":")) parameterName = parameterName.Replace(":",""); + + //HACK if object is null, it is turned into a string, there are no nullable type till now + if (parameterObject == null) parameterObject = ""; + + NpgsqlParameter parameter = new NpgsqlParameter(parameterName, DbtypeFromType(parameterObject.GetType())); + + if (parameterOut) + { + parameter.Direction = ParameterDirection.Output; + } + else + { + parameter.Direction = ParameterDirection.Input; + parameter.Value = CreateParameterValue(parameterObject); + } + + return parameter; + } + + /// + /// Create a parameter with PGSQL schema type + /// + /// + /// + /// + /// + internal NpgsqlParameter CreateParameter(string parameterName, object parameterObject, string PGFieldType) + { + //Tweak so we dont always have to add : sign + if (parameterName.StartsWith(":")) parameterName = parameterName.Replace(":", ""); + + //HACK if object is null, it is turned into a string, there are no nullable type till now + if (parameterObject == null) parameterObject = ""; + + NpgsqlParameter parameter = new NpgsqlParameter(parameterName, DbtypeFromString(parameterObject.GetType(), PGFieldType)); + + parameter.Direction = ParameterDirection.Input; + parameter.Value = CreateParameterValue(parameterObject, PGFieldType); + + return parameter; + } + + /// + /// Checks if we need to do some migrations to the database + /// + /// migrationStore. + public void CheckMigration(string migrationStore) + { + using (NpgsqlConnection connection = new NpgsqlConnection(connectionString)) + { + connection.Open(); + Assembly assem = GetType().Assembly; + PGSQLMigration migration = new PGSQLMigration(connection, assem, migrationStore); + + migration.Update(); + } + } + + /// + /// Returns the version of this DB provider + /// + /// A string containing the DB provider + public string getVersion() + { + Module module = GetType().Module; + // string dllName = module.Assembly.ManifestModule.Name; + Version dllVersion = module.Assembly.GetName().Version; + + return + string.Format("{0}.{1}.{2}.{3}", dllVersion.Major, dllVersion.Minor, dllVersion.Build, + dllVersion.Revision); + } + } +} diff --git a/OpenSim/Data/PGSQL/PGSQLMigration.cs b/OpenSim/Data/PGSQL/PGSQLMigration.cs new file mode 100644 index 0000000000..709fde04f2 --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLMigration.cs @@ -0,0 +1,102 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using Npgsql; +using System; +using System.Data; +using System.Data.Common; +using System.Reflection; + +namespace OpenSim.Data.PGSQL +{ + public class PGSQLMigration : Migration + { + public PGSQLMigration(NpgsqlConnection conn, Assembly assem, string type) + : base(conn, assem, type) + { + } + + public PGSQLMigration(NpgsqlConnection conn, Assembly assem, string subtype, string type) + : base(conn, assem, subtype, type) + { + } + + protected override int FindVersion(DbConnection conn, string type) + { + int version = 0; + NpgsqlConnection lcConn = (NpgsqlConnection)conn; + + using (NpgsqlCommand cmd = lcConn.CreateCommand()) + { + try + { + cmd.CommandText = "select version from migrations where name = '" + type + "' " + + " order by version desc limit 1"; //Must be + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + if (reader.Read()) + { + version = Convert.ToInt32(reader["version"]); + } + reader.Close(); + } + } + catch + { + // Return -1 to indicate table does not exist + return -1; + } + } + return version; + } + + protected override void ExecuteScript(DbConnection conn, string[] script) + { + if (!(conn is NpgsqlConnection)) + { + base.ExecuteScript(conn, script); + return; + } + + foreach (string sql in script) + { + try + { + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, (NpgsqlConnection)conn)) + { + cmd.ExecuteNonQuery(); + } + } + catch (Exception) + { + throw new Exception(sql); + + } + } + } + } +} diff --git a/OpenSim/Data/PGSQL/PGSQLOfflineIMData.cs b/OpenSim/Data/PGSQL/PGSQLOfflineIMData.cs new file mode 100644 index 0000000000..82e5ed829e --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLOfflineIMData.cs @@ -0,0 +1,56 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using OpenSim.Framework; +using OpenMetaverse; +using Npgsql; + +namespace OpenSim.Data.PGSQL +{ + public class PGSQLOfflineIMData : PGSQLGenericTableHandler, IOfflineIMData + { + public PGSQLOfflineIMData(string connectionString, string realm) + : base(connectionString, realm, "IM_Store") + { + } + + public void DeleteOld() + { + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + cmd.CommandText = String.Format("delete from {0} where \"TMStamp\" < CURRENT_DATE - INTERVAL '2 week'", m_Realm); + + ExecuteNonQuery(cmd); + } + + } + } +} diff --git a/OpenSim/Data/PGSQL/PGSQLPresenceData.cs b/OpenSim/Data/PGSQL/PGSQLPresenceData.cs new file mode 100644 index 0000000000..666de07c0d --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLPresenceData.cs @@ -0,0 +1,115 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Data; +using System.Reflection; +using System.Threading; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; +using Npgsql; + +namespace OpenSim.Data.PGSQL +{ + /// + /// A PGSQL Interface for the Presence Server + /// + public class PGSQLPresenceData : PGSQLGenericTableHandler, + IPresenceData + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public PGSQLPresenceData(string connectionString, string realm) : + base(connectionString, realm, "Presence") + { + } + + public PresenceData Get(UUID sessionID) + { + PresenceData[] ret = Get("SessionID", sessionID.ToString()); + + if (ret.Length == 0) + return null; + + return ret[0]; + } + + public void LogoutRegionAgents(UUID regionID) + { + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + + cmd.CommandText = String.Format(@"DELETE FROM {0} WHERE ""RegionID""=:RegionID", m_Realm); + + cmd.Parameters.Add(m_database.CreateParameter("RegionID", regionID)); + cmd.Connection = conn; + conn.Open(); + cmd.ExecuteNonQuery(); + } + } + + public bool ReportAgent(UUID sessionID, UUID regionID) + { + PresenceData[] pd = Get("SessionID", sessionID.ToString()); + if (pd.Length == 0) + return false; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + + cmd.CommandText = String.Format(@"UPDATE {0} SET + ""RegionID"" = :RegionID + WHERE ""SessionID"" = :SessionID", m_Realm); + + cmd.Parameters.Add(m_database.CreateParameter("SessionID", sessionID)); + cmd.Parameters.Add(m_database.CreateParameter("RegionID", regionID)); + cmd.Connection = conn; + conn.Open(); + if (cmd.ExecuteNonQuery() == 0) + return false; + } + return true; + } + + public bool VerifyAgent(UUID agentId, UUID secureSessionID) + { + PresenceData[] ret = Get("SecureSessionID", secureSessionID.ToString()); + + if (ret.Length == 0) + return false; + + if(ret[0].UserID != agentId.ToString()) + return false; + + return true; + } + } +} diff --git a/OpenSim/Data/PGSQL/PGSQLRegionData.cs b/OpenSim/Data/PGSQL/PGSQLRegionData.cs new file mode 100644 index 0000000000..b3076f0756 --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLRegionData.cs @@ -0,0 +1,392 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Data; +using System.Drawing; +using System.IO; +using System.Reflection; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using RegionFlags = OpenSim.Framework.RegionFlags; +using Npgsql; + +namespace OpenSim.Data.PGSQL +{ + /// + /// A PGSQL Interface for the Region Server. + /// + public class PGSQLRegionData : IRegionData + { + private string m_Realm; + private List m_ColumnNames = null; + private string m_ConnectionString; + private PGSQLManager m_database; + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected Dictionary m_FieldTypes = new Dictionary(); + + protected virtual Assembly Assembly + { + get { return GetType().Assembly; } + } + + public PGSQLRegionData(string connectionString, string realm) + { + m_Realm = realm; + m_ConnectionString = connectionString; + m_database = new PGSQLManager(connectionString); + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + { + conn.Open(); + Migration m = new Migration(conn, GetType().Assembly, "GridStore"); + m.Update(); + } + LoadFieldTypes(); + } + + private void LoadFieldTypes() + { + m_FieldTypes = new Dictionary(); + + string query = string.Format(@"select column_name,data_type + from INFORMATION_SCHEMA.COLUMNS + where table_name = lower('{0}'); + + ", m_Realm); + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(query, conn)) + { + conn.Open(); + using (NpgsqlDataReader rdr = cmd.ExecuteReader()) + { + while (rdr.Read()) + { + // query produces 0 to many rows of single column, so always add the first item in each row + m_FieldTypes.Add((string)rdr[0], (string)rdr[1]); + } + } + } + } + + public List Get(string regionName, UUID scopeID) + { + string sql = "select * from "+m_Realm+" where lower(\"regionName\") like lower(:regionName) "; + if (scopeID != UUID.Zero) + sql += " and \"ScopeID\" = :scopeID"; + sql += " order by lower(\"regionName\")"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(m_database.CreateParameter("regionName", regionName)); + if (scopeID != UUID.Zero) + cmd.Parameters.Add(m_database.CreateParameter("scopeID", scopeID)); + conn.Open(); + return RunCommand(cmd); + } + } + + public RegionData Get(int posX, int posY, UUID scopeID) + { + string sql = "select * from "+m_Realm+" where \"locX\" = :posX and \"locY\" = :posY"; + if (scopeID != UUID.Zero) + sql += " and \"ScopeID\" = :scopeID"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(m_database.CreateParameter("posX", posX)); + cmd.Parameters.Add(m_database.CreateParameter("posY", posY)); + if (scopeID != UUID.Zero) + cmd.Parameters.Add(m_database.CreateParameter("scopeID", scopeID)); + conn.Open(); + List ret = RunCommand(cmd); + if (ret.Count == 0) + return null; + + return ret[0]; + } + } + + public RegionData Get(UUID regionID, UUID scopeID) + { + string sql = "select * from "+m_Realm+" where uuid = :regionID"; + if (scopeID != UUID.Zero) + sql += " and \"ScopeID\" = :scopeID"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(m_database.CreateParameter("regionID", regionID)); + if (scopeID != UUID.Zero) + cmd.Parameters.Add(m_database.CreateParameter("scopeID", scopeID)); + conn.Open(); + List ret = RunCommand(cmd); + if (ret.Count == 0) + return null; + + return ret[0]; + } + } + + public List Get(int startX, int startY, int endX, int endY, UUID scopeID) + { + string sql = "select * from "+m_Realm+" where \"locX\" between :startX and :endX and \"locY\" between :startY and :endY"; + if (scopeID != UUID.Zero) + sql += " and \"ScopeID\" = :scopeID"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(m_database.CreateParameter("startX", startX)); + cmd.Parameters.Add(m_database.CreateParameter("startY", startY)); + cmd.Parameters.Add(m_database.CreateParameter("endX", endX)); + cmd.Parameters.Add(m_database.CreateParameter("endY", endY)); + cmd.Parameters.Add(m_database.CreateParameter("scopeID", scopeID)); + conn.Open(); + return RunCommand(cmd); + } + } + + public List RunCommand(NpgsqlCommand cmd) + { + List retList = new List(); + + NpgsqlDataReader result = cmd.ExecuteReader(); + + while (result.Read()) + { + RegionData ret = new RegionData(); + ret.Data = new Dictionary(); + + UUID regionID; + UUID.TryParse(result["uuid"].ToString(), out regionID); + ret.RegionID = regionID; + UUID scope; + UUID.TryParse(result["ScopeID"].ToString(), out scope); + ret.ScopeID = scope; + ret.RegionName = result["regionName"].ToString(); + ret.posX = Convert.ToInt32(result["locX"]); + ret.posY = Convert.ToInt32(result["locY"]); + ret.sizeX = Convert.ToInt32(result["sizeX"]); + ret.sizeY = Convert.ToInt32(result["sizeY"]); + + if (m_ColumnNames == null) + { + m_ColumnNames = new List(); + + DataTable schemaTable = result.GetSchemaTable(); + foreach (DataRow row in schemaTable.Rows) + m_ColumnNames.Add(row["ColumnName"].ToString()); + } + + foreach (string s in m_ColumnNames) + { + if (s == "uuid") + continue; + if (s == "ScopeID") + continue; + if (s == "regionName") + continue; + if (s == "locX") + continue; + if (s == "locY") + continue; + + ret.Data[s] = result[s].ToString(); + } + + retList.Add(ret); + } + return retList; + } + + public bool Store(RegionData data) + { + if (data.Data.ContainsKey("uuid")) + data.Data.Remove("uuid"); + if (data.Data.ContainsKey("ScopeID")) + data.Data.Remove("ScopeID"); + if (data.Data.ContainsKey("regionName")) + data.Data.Remove("regionName"); + if (data.Data.ContainsKey("posX")) + data.Data.Remove("posX"); + if (data.Data.ContainsKey("posY")) + data.Data.Remove("posY"); + if (data.Data.ContainsKey("sizeX")) + data.Data.Remove("sizeX"); + if (data.Data.ContainsKey("sizeY")) + data.Data.Remove("sizeY"); + if (data.Data.ContainsKey("locX")) + data.Data.Remove("locX"); + if (data.Data.ContainsKey("locY")) + data.Data.Remove("locY"); + + string[] fields = new List(data.Data.Keys).ToArray(); + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + + string update = "update " + m_Realm + " set \"locX\"=:posX, \"locY\"=:posY, \"sizeX\"=:sizeX, \"sizeY\"=:sizeY "; + + foreach (string field in fields) + { + + update += ", "; + update += " \"" + field + "\" = :" + field; + + if (m_FieldTypes.ContainsKey(field)) + cmd.Parameters.Add(m_database.CreateParameter(field, data.Data[field], m_FieldTypes[field])); + else + cmd.Parameters.Add(m_database.CreateParameter(field, data.Data[field])); + } + + update += " where uuid = :regionID"; + + if (data.ScopeID != UUID.Zero) + update += " and \"ScopeID\" = :scopeID"; + + cmd.CommandText = update; + cmd.Connection = conn; + cmd.Parameters.Add(m_database.CreateParameter("regionID", data.RegionID)); + cmd.Parameters.Add(m_database.CreateParameter("regionName", data.RegionName)); + cmd.Parameters.Add(m_database.CreateParameter("scopeID", data.ScopeID)); + cmd.Parameters.Add(m_database.CreateParameter("posX", data.posX)); + cmd.Parameters.Add(m_database.CreateParameter("posY", data.posY)); + cmd.Parameters.Add(m_database.CreateParameter("sizeX", data.sizeX)); + cmd.Parameters.Add(m_database.CreateParameter("sizeY", data.sizeY)); + conn.Open(); + try + { + if (cmd.ExecuteNonQuery() < 1) + { + string insert = "insert into " + m_Realm + " (uuid, \"ScopeID\", \"locX\", \"locY\", \"sizeX\", \"sizeY\", \"regionName\", \"" + + String.Join("\", \"", fields) + + "\") values (:regionID, :scopeID, :posX, :posY, :sizeX, :sizeY, :regionName, :" + String.Join(", :", fields) + ")"; + + cmd.CommandText = insert; + + try + { + if (cmd.ExecuteNonQuery() < 1) + { + return false; + } + } + catch (Exception ex) + { + m_log.Warn("[PGSQL Grid]: Error inserting into Regions table: " + ex.Message + ", INSERT sql: " + insert); + } + } + } + catch (Exception ex) + { + m_log.Warn("[PGSQL Grid]: Error updating Regions table: " + ex.Message + ", UPDATE sql: " + update); + } + } + + return true; + } + + public bool SetDataItem(UUID regionID, string item, string value) + { + string sql = "update " + m_Realm + + " set \"" + item + "\" = :" + item + " where uuid = :UUID"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(m_database.CreateParameter("" + item, value)); + cmd.Parameters.Add(m_database.CreateParameter("UUID", regionID)); + conn.Open(); + if (cmd.ExecuteNonQuery() > 0) + return true; + } + return false; + } + + public bool Delete(UUID regionID) + { + string sql = "delete from " + m_Realm + + " where uuid = :UUID"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(m_database.CreateParameter("UUID", regionID)); + conn.Open(); + if (cmd.ExecuteNonQuery() > 0) + return true; + } + return false; + } + + public List GetDefaultRegions(UUID scopeID) + { + return Get((int)RegionFlags.DefaultRegion, scopeID); + } + + public List GetDefaultHypergridRegions(UUID scopeID) + { + return Get((int)RegionFlags.DefaultHGRegion, scopeID); + } + + public List GetFallbackRegions(UUID scopeID, int x, int y) + { + List regions = Get((int)RegionFlags.FallbackRegion, scopeID); + RegionDataDistanceCompare distanceComparer = new RegionDataDistanceCompare(x, y); + regions.Sort(distanceComparer); + + return regions; + } + + public List GetHyperlinks(UUID scopeID) + { + return Get((int)RegionFlags.Hyperlink, scopeID); + } + + private List Get(int regionFlags, UUID scopeID) + { + string sql = "SELECT * FROM " + m_Realm + " WHERE (\"flags\" & " + regionFlags.ToString() + ") <> 0"; + if (scopeID != UUID.Zero) + sql += " AND \"ScopeID\" = :scopeID"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(m_database.CreateParameter("scopeID", scopeID)); + conn.Open(); + return RunCommand(cmd); + } + } + } +} diff --git a/OpenSim/Data/PGSQL/PGSQLSimulationData.cs b/OpenSim/Data/PGSQL/PGSQLSimulationData.cs new file mode 100644 index 0000000000..3243635a3a --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLSimulationData.cs @@ -0,0 +1,2253 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Data; +using System.Drawing; +using System.IO; +using System.Reflection; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using Npgsql; + +namespace OpenSim.Data.PGSQL +{ + /// + /// A PGSQL Interface for the Region Server. + /// + public class PGSQLSimulationData : ISimulationDataStore + { + private const string _migrationStore = "RegionStore"; + + // private static FileSystemDataStore Instance = new FileSystemDataStore(); + private static readonly ILog _Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// The database manager + /// + private PGSQLManager _Database; + private string m_connectionString; + protected virtual Assembly Assembly + { + get { return GetType().Assembly; } + } + + public PGSQLSimulationData() + { + } + + public PGSQLSimulationData(string connectionString) + { + Initialise(connectionString); + } + + /// + /// Initialises the region datastore + /// + /// The connection string. + public void Initialise(string connectionString) + { + m_connectionString = connectionString; + _Database = new PGSQLManager(connectionString); + + using (NpgsqlConnection conn = new NpgsqlConnection(connectionString)) + { + conn.Open(); + //New Migration settings + Migration m = new Migration(conn, Assembly, "RegionStore"); + m.Update(); + } + } + + /// + /// Dispose the database + /// + public void Dispose() { } + + #region SceneObjectGroup region for loading and Store of the scene. + + /// + /// Loads the objects present in the region. + /// + /// The region UUID. + /// + public List LoadObjects(UUID regionUUID) + { + UUID lastGroupID = UUID.Zero; + + Dictionary prims = new Dictionary(); + Dictionary objects = new Dictionary(); + SceneObjectGroup grp = null; + + string sql = @"SELECT *, + CASE WHEN prims.""UUID"" = prims.""SceneGroupID"" THEN 0 ELSE 1 END as sort + FROM prims + LEFT JOIN primshapes ON prims.""UUID"" = primshapes.""UUID"" + WHERE ""RegionUUID"" = :RegionUUID + ORDER BY ""SceneGroupID"" asc, sort asc, ""LinkNumber"" asc"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand command = new NpgsqlCommand(sql, conn)) + { + command.Parameters.Add(_Database.CreateParameter("regionUUID", regionUUID)); + conn.Open(); + using (NpgsqlDataReader reader = command.ExecuteReader()) + { + while (reader.Read()) + { + SceneObjectPart sceneObjectPart = BuildPrim(reader); + if (reader["Shape"] is DBNull) + sceneObjectPart.Shape = PrimitiveBaseShape.Default; + else + sceneObjectPart.Shape = BuildShape(reader); + + prims[sceneObjectPart.UUID] = sceneObjectPart; + + UUID groupID = new UUID((Guid)reader["SceneGroupID"]); + + if (groupID != lastGroupID) // New SOG + { + if (grp != null) + objects[grp.UUID] = grp; + + lastGroupID = groupID; + + // There sometimes exist OpenSim bugs that 'orphan groups' so that none of the prims are + // recorded as the root prim (for which the UUID must equal the persisted group UUID). In + // this case, force the UUID to be the same as the group UUID so that at least these can be + // deleted (we need to change the UUID so that any other prims in the linkset can also be + // deleted). + if (sceneObjectPart.UUID != groupID && groupID != UUID.Zero) + { + _Log.WarnFormat( + "[REGION DB]: Found root prim {0} {1} at {2} where group was actually {3}. Forcing UUID to group UUID", + sceneObjectPart.Name, sceneObjectPart.UUID, sceneObjectPart.GroupPosition, groupID); + + sceneObjectPart.UUID = groupID; + } + + grp = new SceneObjectGroup(sceneObjectPart); + } + else + { + // Black magic to preserve link numbers + // Why is this needed, fix this in AddPart method. + int link = sceneObjectPart.LinkNum; + + grp.AddPart(sceneObjectPart); + + if (link != 0) + sceneObjectPart.LinkNum = link; + } + } + } + } + + if (grp != null) + objects[grp.UUID] = grp; + + // Instead of attempting to LoadItems on every prim, + // most of which probably have no items... get a + // list from DB of all prims which have items and + // LoadItems only on those + List primsWithInventory = new List(); + string qry = "select distinct \"primID\" from primitems"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand command = new NpgsqlCommand(qry, conn)) + { + conn.Open(); + using (NpgsqlDataReader itemReader = command.ExecuteReader()) + { + while (itemReader.Read()) + { + if (!(itemReader["primID"] is DBNull)) + { + UUID primID = new UUID(itemReader["primID"].ToString()); + if (prims.ContainsKey(primID)) + { + primsWithInventory.Add(prims[primID]); + } + } + } + } + } + + LoadItems(primsWithInventory); + + _Log.DebugFormat("[REGION DB]: Loaded {0} objects using {1} prims", objects.Count, prims.Count); + + return new List(objects.Values); + } + + /// + /// Load in the prim's persisted inventory. + /// + /// all prims with inventory on a region + private void LoadItems(List allPrimsWithInventory) + { + string sql = @"SELECT * FROM primitems WHERE ""primID"" = :PrimID"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand command = new NpgsqlCommand(sql, conn)) + { + conn.Open(); + foreach (SceneObjectPart objectPart in allPrimsWithInventory) + { + command.Parameters.Clear(); + command.Parameters.Add(_Database.CreateParameter("PrimID", objectPart.UUID)); + + List inventory = new List(); + + using (NpgsqlDataReader reader = command.ExecuteReader()) + { + while (reader.Read()) + { + TaskInventoryItem item = BuildItem(reader); + + item.ParentID = objectPart.UUID; // Values in database are + // often wrong + inventory.Add(item); + } + } + + objectPart.Inventory.RestoreInventoryItems(inventory); + } + } + } + + /// + /// Stores all object's details apart from inventory + /// + /// + /// + public void StoreObject(SceneObjectGroup obj, UUID regionUUID) + { + uint flags = obj.RootPart.GetEffectiveObjectFlags(); + // Eligibility check + // + if ((flags & (uint)PrimFlags.Temporary) != 0) + return; + if ((flags & (uint)PrimFlags.TemporaryOnRez) != 0) + return; + + //_Log.DebugFormat("[PGSQL]: Adding/Changing SceneObjectGroup: {0} to region: {1}, object has {2} prims.", obj.UUID, regionUUID, obj.Parts.Length); + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + { + conn.Open(); + NpgsqlTransaction transaction = conn.BeginTransaction(); + + try + { + foreach (SceneObjectPart sceneObjectPart in obj.Parts) + { + //Update prim + using (NpgsqlCommand sqlCommand = conn.CreateCommand()) + { + sqlCommand.Transaction = transaction; + try + { + StoreSceneObjectPrim(sceneObjectPart, sqlCommand, obj.UUID, regionUUID); + } + catch (NpgsqlException sqlEx) + { + _Log.ErrorFormat("[REGION DB]: Store SceneObjectPrim SQL error: {0} at line {1}", sqlEx.Message, sqlEx.Line); + throw; + } + } + + //Update primshapes + using (NpgsqlCommand sqlCommand = conn.CreateCommand()) + { + sqlCommand.Transaction = transaction; + try + { + StoreSceneObjectPrimShapes(sceneObjectPart, sqlCommand, obj.UUID, regionUUID); + } + catch (NpgsqlException sqlEx) + { + _Log.ErrorFormat("[REGION DB]: Store SceneObjectPrimShapes SQL error: {0} at line {1}", sqlEx.Message, sqlEx.Line); + throw; + } + } + } + + transaction.Commit(); + } + catch (Exception ex) + { + _Log.ErrorFormat("[REGION DB]: Store SceneObjectGroup error: {0}, Rolling back...", ex.Message); + try + { + transaction.Rollback(); + } + catch (Exception ex2) + { + //Show error + _Log.InfoFormat("[REGION DB]: Rollback of SceneObjectGroup store transaction failed with error: {0}", ex2.Message); + + } + } + } + } + + /// + /// Stores the prim of the sceneobjectpart. + /// + /// The sceneobjectpart or prim. + /// The SQL command with the transaction. + /// The scenegroup UUID. + /// The region UUID. + private void StoreSceneObjectPrim(SceneObjectPart sceneObjectPart, NpgsqlCommand sqlCommand, UUID sceneGroupID, UUID regionUUID) + { + //Big query to update or insert a new prim. + + string queryPrims = @" + UPDATE prims SET + ""CreationDate"" = :CreationDate, ""Name"" = :Name, ""Text"" = :Text, ""Description"" = :Description, ""SitName"" = :SitName, + ""TouchName"" = :TouchName, ""ObjectFlags"" = :ObjectFlags, ""OwnerMask"" = :OwnerMask, ""NextOwnerMask"" = :NextOwnerMask, ""GroupMask"" = :GroupMask, + ""EveryoneMask"" = :EveryoneMask, ""BaseMask"" = :BaseMask, ""PositionX"" = :PositionX, ""PositionY"" = :PositionY, ""PositionZ"" = :PositionZ, + ""GroupPositionX"" = :GroupPositionX, ""GroupPositionY"" = :GroupPositionY, ""GroupPositionZ"" = :GroupPositionZ, ""VelocityX"" = :VelocityX, + ""VelocityY"" = :VelocityY, ""VelocityZ"" = :VelocityZ, ""AngularVelocityX"" = :AngularVelocityX, ""AngularVelocityY"" = :AngularVelocityY, + ""AngularVelocityZ"" = :AngularVelocityZ, ""AccelerationX"" = :AccelerationX, ""AccelerationY"" = :AccelerationY, + ""AccelerationZ"" = :AccelerationZ, ""RotationX"" = :RotationX, ""RotationY"" = :RotationY, ""RotationZ"" = :RotationZ, ""RotationW"" = :RotationW, + ""SitTargetOffsetX"" = :SitTargetOffsetX, ""SitTargetOffsetY"" = :SitTargetOffsetY, ""SitTargetOffsetZ"" = :SitTargetOffsetZ, + ""SitTargetOrientW"" = :SitTargetOrientW, ""SitTargetOrientX"" = :SitTargetOrientX, ""SitTargetOrientY"" = :SitTargetOrientY, + ""SitTargetOrientZ"" = :SitTargetOrientZ, ""RegionUUID"" = :RegionUUID, ""CreatorID"" = :CreatorID, ""OwnerID"" = :OwnerID, ""GroupID"" = :GroupID, + ""LastOwnerID"" = :LastOwnerID, ""SceneGroupID"" = :SceneGroupID, ""PayPrice"" = :PayPrice, ""PayButton1"" = :PayButton1, ""PayButton2"" = :PayButton2, + ""PayButton3"" = :PayButton3, ""PayButton4"" = :PayButton4, ""LoopedSound"" = :LoopedSound, ""LoopedSoundGain"" = :LoopedSoundGain, + ""TextureAnimation"" = :TextureAnimation, ""OmegaX"" = :OmegaX, ""OmegaY"" = :OmegaY, ""OmegaZ"" = :OmegaZ, ""CameraEyeOffsetX"" = :CameraEyeOffsetX, + ""CameraEyeOffsetY"" = :CameraEyeOffsetY, ""CameraEyeOffsetZ"" = :CameraEyeOffsetZ, ""CameraAtOffsetX"" = :CameraAtOffsetX, + ""CameraAtOffsetY"" = :CameraAtOffsetY, ""CameraAtOffsetZ"" = :CameraAtOffsetZ, ""ForceMouselook"" = :ForceMouselook, + ""ScriptAccessPin"" = :ScriptAccessPin, ""AllowedDrop"" = :AllowedDrop, ""DieAtEdge"" = :DieAtEdge, ""SalePrice"" = :SalePrice, + ""SaleType"" = :SaleType, ""ColorR"" = :ColorR, ""ColorG"" = :ColorG, ""ColorB"" = :ColorB, ""ColorA"" = :ColorA, ""ParticleSystem"" = :ParticleSystem, + ""ClickAction"" = :ClickAction, ""Material"" = :Material, ""CollisionSound"" = :CollisionSound, ""CollisionSoundVolume"" = :CollisionSoundVolume, ""PassTouches"" = :PassTouches, + ""LinkNumber"" = :LinkNumber, ""MediaURL"" = :MediaURL, ""DynAttrs"" = :DynAttrs, + ""PhysicsShapeType"" = :PhysicsShapeType, ""Density"" = :Density, ""GravityModifier"" = :GravityModifier, ""Friction"" = :Friction, ""Restitution"" = :Restitution + WHERE ""UUID"" = :UUID ; + + INSERT INTO + prims ( + ""UUID"", ""CreationDate"", ""Name"", ""Text"", ""Description"", ""SitName"", ""TouchName"", ""ObjectFlags"", ""OwnerMask"", ""NextOwnerMask"", ""GroupMask"", + ""EveryoneMask"", ""BaseMask"", ""PositionX"", ""PositionY"", ""PositionZ"", ""GroupPositionX"", ""GroupPositionY"", ""GroupPositionZ"", ""VelocityX"", + ""VelocityY"", ""VelocityZ"", ""AngularVelocityX"", ""AngularVelocityY"", ""AngularVelocityZ"", ""AccelerationX"", ""AccelerationY"", ""AccelerationZ"", + ""RotationX"", ""RotationY"", ""RotationZ"", ""RotationW"", ""SitTargetOffsetX"", ""SitTargetOffsetY"", ""SitTargetOffsetZ"", ""SitTargetOrientW"", + ""SitTargetOrientX"", ""SitTargetOrientY"", ""SitTargetOrientZ"", ""RegionUUID"", ""CreatorID"", ""OwnerID"", ""GroupID"", ""LastOwnerID"", ""SceneGroupID"", + ""PayPrice"", ""PayButton1"", ""PayButton2"", ""PayButton3"", ""PayButton4"", ""LoopedSound"", ""LoopedSoundGain"", ""TextureAnimation"", ""OmegaX"", + ""OmegaY"", ""OmegaZ"", ""CameraEyeOffsetX"", ""CameraEyeOffsetY"", ""CameraEyeOffsetZ"", ""CameraAtOffsetX"", ""CameraAtOffsetY"", ""CameraAtOffsetZ"", + ""ForceMouselook"", ""ScriptAccessPin"", ""AllowedDrop"", ""DieAtEdge"", ""SalePrice"", ""SaleType"", ""ColorR"", ""ColorG"", ""ColorB"", ""ColorA"", + ""ParticleSystem"", ""ClickAction"", ""Material"", ""CollisionSound"", ""CollisionSoundVolume"", ""PassTouches"", ""LinkNumber"", ""MediaURL"", ""DynAttrs"", + ""PhysicsShapeType"", ""Density"", ""GravityModifier"", ""Friction"", ""Restitution"" + ) Select + :UUID, :CreationDate, :Name, :Text, :Description, :SitName, :TouchName, :ObjectFlags, :OwnerMask, :NextOwnerMask, :GroupMask, + :EveryoneMask, :BaseMask, :PositionX, :PositionY, :PositionZ, :GroupPositionX, :GroupPositionY, :GroupPositionZ, :VelocityX, + :VelocityY, :VelocityZ, :AngularVelocityX, :AngularVelocityY, :AngularVelocityZ, :AccelerationX, :AccelerationY, :AccelerationZ, + :RotationX, :RotationY, :RotationZ, :RotationW, :SitTargetOffsetX, :SitTargetOffsetY, :SitTargetOffsetZ, :SitTargetOrientW, + :SitTargetOrientX, :SitTargetOrientY, :SitTargetOrientZ, :RegionUUID, :CreatorID, :OwnerID, :GroupID, :LastOwnerID, :SceneGroupID, + :PayPrice, :PayButton1, :PayButton2, :PayButton3, :PayButton4, :LoopedSound, :LoopedSoundGain, :TextureAnimation, :OmegaX, + :OmegaY, :OmegaZ, :CameraEyeOffsetX, :CameraEyeOffsetY, :CameraEyeOffsetZ, :CameraAtOffsetX, :CameraAtOffsetY, :CameraAtOffsetZ, + :ForceMouselook, :ScriptAccessPin, :AllowedDrop, :DieAtEdge, :SalePrice, :SaleType, :ColorR, :ColorG, :ColorB, :ColorA, + :ParticleSystem, :ClickAction, :Material, :CollisionSound, :CollisionSoundVolume, :PassTouches, :LinkNumber, :MediaURL, :DynAttrs, + :PhysicsShapeType, :Density, :GravityModifier, :Friction, :Restitution + where not EXISTS (SELECT ""UUID"" FROM prims WHERE ""UUID"" = :UUID); + "; + + //Set commandtext. + sqlCommand.CommandText = queryPrims; + //Add parameters + sqlCommand.Parameters.AddRange(CreatePrimParameters(sceneObjectPart, sceneGroupID, regionUUID)); + + //Execute the query. If it fails then error is trapped in calling function + sqlCommand.ExecuteNonQuery(); + } + + /// + /// Stores the scene object prim shapes. + /// + /// The sceneobjectpart containing prim shape. + /// The SQL command with the transaction. + /// The scenegroup UUID. + /// The region UUID. + private void StoreSceneObjectPrimShapes(SceneObjectPart sceneObjectPart, NpgsqlCommand sqlCommand, UUID sceneGroupID, UUID regionUUID) + { + //Big query to or insert or update primshapes + + string queryPrimShapes = @" + UPDATE primshapes SET + ""Shape"" = :Shape, ""ScaleX"" = :ScaleX, ""ScaleY"" = :ScaleY, ""ScaleZ"" = :ScaleZ, ""PCode"" = :PCode, ""PathBegin"" = :PathBegin, + ""PathEnd"" = :PathEnd, ""PathScaleX"" = :PathScaleX, ""PathScaleY"" = :PathScaleY, ""PathShearX"" = :PathShearX, ""PathShearY"" = :PathShearY, + ""PathSkew"" = :PathSkew, ""PathCurve"" = :PathCurve, ""PathRadiusOffset"" = :PathRadiusOffset, ""PathRevolutions"" = :PathRevolutions, + ""PathTaperX"" = :PathTaperX, ""PathTaperY"" = :PathTaperY, ""PathTwist"" = :PathTwist, ""PathTwistBegin"" = :PathTwistBegin, + ""ProfileBegin"" = :ProfileBegin, ""ProfileEnd"" = :ProfileEnd, ""ProfileCurve"" = :ProfileCurve, ""ProfileHollow"" = :ProfileHollow, + ""Texture"" = :Texture, ""ExtraParams"" = :ExtraParams, ""State"" = :State, ""Media"" = :Media + WHERE ""UUID"" = :UUID ; + + INSERT INTO + primshapes ( + ""UUID"", ""Shape"", ""ScaleX"", ""ScaleY"", ""ScaleZ"", ""PCode"", ""PathBegin"", ""PathEnd"", ""PathScaleX"", ""PathScaleY"", ""PathShearX"", ""PathShearY"", + ""PathSkew"", ""PathCurve"", ""PathRadiusOffset"", ""PathRevolutions"", ""PathTaperX"", ""PathTaperY"", ""PathTwist"", ""PathTwistBegin"", ""ProfileBegin"", + ""ProfileEnd"", ""ProfileCurve"", ""ProfileHollow"", ""Texture"", ""ExtraParams"", ""State"", ""Media"" + ) + Select + :UUID, :Shape, :ScaleX, :ScaleY, :ScaleZ, :PCode, :PathBegin, :PathEnd, :PathScaleX, :PathScaleY, :PathShearX, :PathShearY, + :PathSkew, :PathCurve, :PathRadiusOffset, :PathRevolutions, :PathTaperX, :PathTaperY, :PathTwist, :PathTwistBegin, :ProfileBegin, + :ProfileEnd, :ProfileCurve, :ProfileHollow, :Texture, :ExtraParams, :State, :Media + where not EXISTS (SELECT ""UUID"" FROM primshapes WHERE ""UUID"" = :UUID); + "; + + //Set commandtext. + sqlCommand.CommandText = queryPrimShapes; + + //Add parameters + sqlCommand.Parameters.AddRange(CreatePrimShapeParameters(sceneObjectPart, sceneGroupID, regionUUID)); + + //Execute the query. If it fails then error is trapped in calling function + sqlCommand.ExecuteNonQuery(); + + } + + /// + /// Removes a object from the database. + /// Meaning removing it from tables Prims, PrimShapes and PrimItems + /// + /// id of scenegroup + /// regionUUID (is this used anyway + public void RemoveObject(UUID objectID, UUID regionUUID) + { + //_Log.InfoFormat("[PGSQL]: Removing obj: {0} from region: {1}", objectID, regionUUID); + + //Remove from prims and primsitem table + string sqlPrims = @"DELETE FROM PRIMS WHERE ""SceneGroupID"" = :objectID"; + string sqlPrimItems = @"DELETE FROM PRIMITEMS WHERE ""primID"" in (SELECT ""UUID"" FROM PRIMS WHERE ""SceneGroupID"" = :objectID)"; + string sqlPrimShapes = @"DELETE FROM PRIMSHAPES WHERE ""UUID"" in (SELECT ""UUID"" FROM PRIMS WHERE ""SceneGroupID"" = :objectID)"; + + lock (_Database) + { + //Using the non transaction mode. + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + cmd.Connection = conn; + cmd.CommandText = sqlPrimShapes; + conn.Open(); + cmd.Parameters.Add(_Database.CreateParameter("objectID", objectID)); + cmd.ExecuteNonQuery(); + + cmd.CommandText = sqlPrimItems; + cmd.ExecuteNonQuery(); + + cmd.CommandText = sqlPrims; + cmd.ExecuteNonQuery(); + } + } + } + + /// + /// Store the inventory of a prim. Warning deletes everything first and then adds all again. + /// + /// + /// + public void StorePrimInventory(UUID primID, ICollection items) + { + //_Log.InfoFormat("[REGION DB: Persisting Prim Inventory with prim ID {0}", primID); + + //Statement from PGSQL section! + // For now, we're just going to crudely remove all the previous inventory items + // no matter whether they have changed or not, and replace them with the current set. + + //Delete everything from PrimID + //TODO add index on PrimID in DB, if not already exist + + string sql = @"delete from primitems where ""primID"" = :primID"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("primID", primID)); + conn.Open(); + cmd.ExecuteNonQuery(); + } + + sql = + @"INSERT INTO primitems ( + ""itemID"",""primID"",""assetID"",""parentFolderID"",""invType"",""assetType"",""name"",""description"",""creationDate"",""creatorID"",""ownerID"",""lastOwnerID"",""groupID"", + ""nextPermissions"",""currentPermissions"",""basePermissions"",""everyonePermissions"",""groupPermissions"",""flags"") + VALUES (:itemID,:primID,:assetID,:parentFolderID,:invType,:assetType,:name,:description,:creationDate,:creatorID,:ownerID, + :lastOwnerID,:groupID,:nextPermissions,:currentPermissions,:basePermissions,:everyonePermissions,:groupPermissions,:flags)"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + conn.Open(); + foreach (TaskInventoryItem taskItem in items) + { + cmd.Parameters.AddRange(CreatePrimInventoryParameters(taskItem)); + cmd.ExecuteNonQuery(); + cmd.Parameters.Clear(); + } + } + } + + #endregion + + /// + /// Loads the terrain map. + /// + /// regionID. + /// + public double[,] LoadTerrain(UUID regionID) + { + double[,] terrain = new double[(int)Constants.RegionSize, (int)Constants.RegionSize]; + terrain.Initialize(); + + string sql = @"select ""RegionUUID"", ""Revision"", ""Heightfield"" from terrain + where ""RegionUUID"" = :RegionUUID order by ""Revision"" desc limit 1; "; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + // PGSqlParameter param = new PGSqlParameter(); + cmd.Parameters.Add(_Database.CreateParameter("RegionUUID", regionID)); + conn.Open(); + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + int rev; + if (reader.Read()) + { + MemoryStream str = new MemoryStream((byte[])reader["Heightfield"]); + BinaryReader br = new BinaryReader(str); + for (int x = 0; x < (int)Constants.RegionSize; x++) + { + for (int y = 0; y < (int)Constants.RegionSize; y++) + { + terrain[x, y] = br.ReadDouble(); + } + } + rev = (int)reader["Revision"]; + } + else + { + _Log.Info("[REGION DB]: No terrain found for region"); + return null; + } + _Log.Info("[REGION DB]: Loaded terrain revision r" + rev); + } + } + + return terrain; + } + + /// + /// Stores the terrain map to DB. + /// + /// terrain map data. + /// regionID. + public void StoreTerrain(double[,] terrain, UUID regionID) + { + int revision = Util.UnixTimeSinceEpoch(); + + //Delete old terrain map + string sql = @"delete from terrain where ""RegionUUID""=:RegionUUID"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("RegionUUID", regionID)); + conn.Open(); + cmd.ExecuteNonQuery(); + } + + _Log.Info("[REGION DB]: Deleted terrain revision r " + revision); + + sql = @"insert into terrain(""RegionUUID"", ""Revision"", ""Heightfield"") values(:RegionUUID, :Revision, :Heightfield)"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("RegionUUID", regionID)); + cmd.Parameters.Add(_Database.CreateParameter("Revision", revision)); + cmd.Parameters.Add(_Database.CreateParameter("Heightfield", serializeTerrain(terrain))); + conn.Open(); + cmd.ExecuteNonQuery(); + } + + _Log.Info("[REGION DB]: Stored terrain revision r " + revision); + } + + /// + /// Loads all the land objects of a region. + /// + /// The region UUID. + /// + public List LoadLandObjects(UUID regionUUID) + { + List LandDataForRegion = new List(); + + string sql = @"select * from land where ""RegionUUID"" = :RegionUUID"; + + //Retrieve all land data from region + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("RegionUUID", regionUUID)); + conn.Open(); + using (NpgsqlDataReader readerLandData = cmd.ExecuteReader()) + { + while (readerLandData.Read()) + { + LandDataForRegion.Add(BuildLandData(readerLandData)); + } + } + } + + //Retrieve all accesslist data for all landdata + foreach (LandData LandData in LandDataForRegion) + { + sql = @"select * from landaccesslist where ""LandUUID"" = :LandUUID"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("LandUUID", LandData.GlobalID)); + conn.Open(); + using (NpgsqlDataReader readerAccessList = cmd.ExecuteReader()) + { + while (readerAccessList.Read()) + { + LandData.ParcelAccessList.Add(BuildLandAccessData(readerAccessList)); + } + } + } + } + + //Return data + return LandDataForRegion; + } + + /// + /// Stores land object with landaccess list. + /// + /// parcel data. + public void StoreLandObject(ILandObject parcel) + { + //As this is only one record in land table I just delete all and then add a new record. + //As the delete landaccess is already in the pgsql code + + //Delete old values + RemoveLandObject(parcel.LandData.GlobalID); + + //Insert new values + string sql = @"INSERT INTO land + (""UUID"",""RegionUUID"",""LocalLandID"",""Bitmap"",""Name"",""Description"",""OwnerUUID"",""IsGroupOwned"",""Area"",""AuctionID"",""Category"",""ClaimDate"",""ClaimPrice"", + ""GroupUUID"",""SalePrice"",""LandStatus"",""LandFlags"",""LandingType"",""MediaAutoScale"",""MediaTextureUUID"",""MediaURL"",""MusicURL"",""PassHours"",""PassPrice"", + ""SnapshotUUID"",""UserLocationX"",""UserLocationY"",""UserLocationZ"",""UserLookAtX"",""UserLookAtY"",""UserLookAtZ"",""AuthbuyerID"",""OtherCleanTime"") + VALUES + (:UUID,:RegionUUID,:LocalLandID,:Bitmap,:Name,:Description,:OwnerUUID,:IsGroupOwned,:Area,:AuctionID,:Category,:ClaimDate,:ClaimPrice, + :GroupUUID,:SalePrice,:LandStatus,:LandFlags,:LandingType,:MediaAutoScale,:MediaTextureUUID,:MediaURL,:MusicURL,:PassHours,:PassPrice, + :SnapshotUUID,:UserLocationX,:UserLocationY,:UserLocationZ,:UserLookAtX,:UserLookAtY,:UserLookAtZ,:AuthbuyerID,:OtherCleanTime)"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.AddRange(CreateLandParameters(parcel.LandData, parcel.RegionUUID)); + conn.Open(); + cmd.ExecuteNonQuery(); + } + + sql = @"INSERT INTO landaccesslist (""LandUUID"",""AccessUUID"",""LandFlags"",""Expires"") VALUES (:LandUUID,:AccessUUID,:Flags,:Expires)"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + conn.Open(); + foreach (LandAccessEntry parcelAccessEntry in parcel.LandData.ParcelAccessList) + { + cmd.Parameters.AddRange(CreateLandAccessParameters(parcelAccessEntry, parcel.RegionUUID)); + + cmd.ExecuteNonQuery(); + cmd.Parameters.Clear(); + } + } + } + + /// + /// Removes a land object from DB. + /// + /// UUID of landobject + public void RemoveLandObject(UUID globalID) + { + string sql = @"delete from land where ""UUID""=:UUID"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("UUID", globalID)); + conn.Open(); + cmd.ExecuteNonQuery(); + } + sql = @"delete from landaccesslist where ""LandUUID""=:UUID"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("UUID", globalID)); + conn.Open(); + cmd.ExecuteNonQuery(); + } + } + public RegionLightShareData LoadRegionWindlightSettings(UUID regionUUID) + { + RegionLightShareData nWP = new RegionLightShareData(); + nWP.OnSave += StoreRegionWindlightSettings; + + string sql = @"select * from regionwindlight where ""region_id"" = :regionID"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("regionID", regionUUID.ToString() )); + conn.Open(); + using (NpgsqlDataReader result = cmd.ExecuteReader()) + { + if (!result.Read()) + { + //No result, so store our default windlight profile and return it + nWP.regionID = regionUUID; + StoreRegionWindlightSettings(nWP); + return nWP; + } + else + { + nWP.regionID = DBGuid.FromDB(result["region_id"]); + nWP.waterColor.X = Convert.ToSingle(result["water_color_r"]); + nWP.waterColor.Y = Convert.ToSingle(result["water_color_g"]); + nWP.waterColor.Z = Convert.ToSingle(result["water_color_b"]); + nWP.waterFogDensityExponent = Convert.ToSingle(result["water_fog_density_exponent"]); + nWP.underwaterFogModifier = Convert.ToSingle(result["underwater_fog_modifier"]); + nWP.reflectionWaveletScale.X = Convert.ToSingle(result["reflection_wavelet_scale_1"]); + nWP.reflectionWaveletScale.Y = Convert.ToSingle(result["reflection_wavelet_scale_2"]); + nWP.reflectionWaveletScale.Z = Convert.ToSingle(result["reflection_wavelet_scale_3"]); + nWP.fresnelScale = Convert.ToSingle(result["fresnel_scale"]); + nWP.fresnelOffset = Convert.ToSingle(result["fresnel_offset"]); + nWP.refractScaleAbove = Convert.ToSingle(result["refract_scale_above"]); + nWP.refractScaleBelow = Convert.ToSingle(result["refract_scale_below"]); + nWP.blurMultiplier = Convert.ToSingle(result["blur_multiplier"]); + nWP.bigWaveDirection.X = Convert.ToSingle(result["big_wave_direction_x"]); + nWP.bigWaveDirection.Y = Convert.ToSingle(result["big_wave_direction_y"]); + nWP.littleWaveDirection.X = Convert.ToSingle(result["little_wave_direction_x"]); + nWP.littleWaveDirection.Y = Convert.ToSingle(result["little_wave_direction_y"]); + UUID.TryParse(result["normal_map_texture"].ToString(), out nWP.normalMapTexture); + nWP.horizon.X = Convert.ToSingle(result["horizon_r"]); + nWP.horizon.Y = Convert.ToSingle(result["horizon_g"]); + nWP.horizon.Z = Convert.ToSingle(result["horizon_b"]); + nWP.horizon.W = Convert.ToSingle(result["horizon_i"]); + nWP.hazeHorizon = Convert.ToSingle(result["haze_horizon"]); + nWP.blueDensity.X = Convert.ToSingle(result["blue_density_r"]); + nWP.blueDensity.Y = Convert.ToSingle(result["blue_density_g"]); + nWP.blueDensity.Z = Convert.ToSingle(result["blue_density_b"]); + nWP.blueDensity.W = Convert.ToSingle(result["blue_density_i"]); + nWP.hazeDensity = Convert.ToSingle(result["haze_density"]); + nWP.densityMultiplier = Convert.ToSingle(result["density_multiplier"]); + nWP.distanceMultiplier = Convert.ToSingle(result["distance_multiplier"]); + nWP.maxAltitude = Convert.ToUInt16(result["max_altitude"]); + nWP.sunMoonColor.X = Convert.ToSingle(result["sun_moon_color_r"]); + nWP.sunMoonColor.Y = Convert.ToSingle(result["sun_moon_color_g"]); + nWP.sunMoonColor.Z = Convert.ToSingle(result["sun_moon_color_b"]); + nWP.sunMoonColor.W = Convert.ToSingle(result["sun_moon_color_i"]); + nWP.sunMoonPosition = Convert.ToSingle(result["sun_moon_position"]); + nWP.ambient.X = Convert.ToSingle(result["ambient_r"]); + nWP.ambient.Y = Convert.ToSingle(result["ambient_g"]); + nWP.ambient.Z = Convert.ToSingle(result["ambient_b"]); + nWP.ambient.W = Convert.ToSingle(result["ambient_i"]); + nWP.eastAngle = Convert.ToSingle(result["east_angle"]); + nWP.sunGlowFocus = Convert.ToSingle(result["sun_glow_focus"]); + nWP.sunGlowSize = Convert.ToSingle(result["sun_glow_size"]); + nWP.sceneGamma = Convert.ToSingle(result["scene_gamma"]); + nWP.starBrightness = Convert.ToSingle(result["star_brightness"]); + nWP.cloudColor.X = Convert.ToSingle(result["cloud_color_r"]); + nWP.cloudColor.Y = Convert.ToSingle(result["cloud_color_g"]); + nWP.cloudColor.Z = Convert.ToSingle(result["cloud_color_b"]); + nWP.cloudColor.W = Convert.ToSingle(result["cloud_color_i"]); + nWP.cloudXYDensity.X = Convert.ToSingle(result["cloud_x"]); + nWP.cloudXYDensity.Y = Convert.ToSingle(result["cloud_y"]); + nWP.cloudXYDensity.Z = Convert.ToSingle(result["cloud_density"]); + nWP.cloudCoverage = Convert.ToSingle(result["cloud_coverage"]); + nWP.cloudScale = Convert.ToSingle(result["cloud_scale"]); + nWP.cloudDetailXYDensity.X = Convert.ToSingle(result["cloud_detail_x"]); + nWP.cloudDetailXYDensity.Y = Convert.ToSingle(result["cloud_detail_y"]); + nWP.cloudDetailXYDensity.Z = Convert.ToSingle(result["cloud_detail_density"]); + nWP.cloudScrollX = Convert.ToSingle(result["cloud_scroll_x"]); + nWP.cloudScrollXLock = Convert.ToBoolean(result["cloud_scroll_x_lock"]); + nWP.cloudScrollY = Convert.ToSingle(result["cloud_scroll_y"]); + nWP.cloudScrollYLock = Convert.ToBoolean(result["cloud_scroll_y_lock"]); + nWP.drawClassicClouds = Convert.ToBoolean(result["draw_classic_clouds"]); + nWP.valid = true; + } + } + } + return nWP; + } + + public void RemoveRegionWindlightSettings(UUID regionID) + { + string sql = @"delete from regionwindlight where ""region_id"" = :region_id"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + conn.Open(); + cmd.Parameters.Add(_Database.CreateParameter("region_id", regionID.ToString())); + cmd.ExecuteNonQuery(); + } + } + + public void StoreRegionWindlightSettings(RegionLightShareData wl) + { + string sql = @"select region_id from regionwindlight where ""region_id"" = :region_id limit 1;"; + bool exists = false; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + { + conn.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("region_id", wl.regionID.ToString() )); + NpgsqlDataReader dr = cmd.ExecuteReader(); + exists = dr.Read(); + } + } + if (exists) + { + RemoveRegionWindlightSettings(wl.regionID); + } + + // sql insert + sql = @"INSERT INTO regionwindlight + (region_id + ,water_color_r + ,water_color_g + ,water_color_b + ,water_fog_density_exponent + ,underwater_fog_modifier + ,reflection_wavelet_scale_1 + ,reflection_wavelet_scale_2 + ,reflection_wavelet_scale_3 + ,fresnel_scale + ,fresnel_offset + ,refract_scale_above + ,refract_scale_below + ,blur_multiplier + ,big_wave_direction_x + ,big_wave_direction_y + ,little_wave_direction_x + ,little_wave_direction_y + ,normal_map_texture + ,horizon_r + ,horizon_g + ,horizon_b + ,horizon_i + ,haze_horizon + ,blue_density_r + ,blue_density_g + ,blue_density_b + ,blue_density_i + ,haze_density + ,density_multiplier + ,distance_multiplier + ,max_altitude + ,sun_moon_color_r + ,sun_moon_color_g + ,sun_moon_color_b + ,sun_moon_color_i + ,sun_moon_position + ,ambient_r + ,ambient_g + ,ambient_b + ,ambient_i + ,east_angle + ,sun_glow_focus + ,sun_glow_size + ,scene_gamma + ,star_brightness + ,cloud_color_r + ,cloud_color_g + ,cloud_color_b + ,cloud_color_i + ,cloud_x + ,cloud_y + ,cloud_density + ,cloud_coverage + ,cloud_scale + ,cloud_detail_x + ,cloud_detail_y + ,cloud_detail_density + ,cloud_scroll_x + ,cloud_scroll_x_lock + ,cloud_scroll_y + ,cloud_scroll_y_lock + ,draw_classic_clouds) + VALUES + (:region_id + ,:water_color_r + ,:water_color_g + ,:water_color_b + ,:water_fog_density_exponent + ,:underwater_fog_modifier + ,:reflection_wavelet_scale_1 + ,:reflection_wavelet_scale_2 + ,:reflection_wavelet_scale_3 + ,:fresnel_scale + ,:fresnel_offset + ,:refract_scale_above + ,:refract_scale_below + ,:blur_multiplier + ,:big_wave_direction_x + ,:big_wave_direction_y + ,:little_wave_direction_x + ,:little_wave_direction_y + ,:normal_map_texture + ,:horizon_r + ,:horizon_g + ,:horizon_b + ,:horizon_i + ,:haze_horizon + ,:blue_density_r + ,:blue_density_g + ,:blue_density_b + ,:blue_density_i + ,:haze_density + ,:density_multiplier + ,:distance_multiplier + ,:max_altitude + ,:sun_moon_color_r + ,:sun_moon_color_g + ,:sun_moon_color_b + ,:sun_moon_color_i + ,:sun_moon_position + ,:ambient_r + ,:ambient_g + ,:ambient_b + ,:ambient_i + ,:east_angle + ,:sun_glow_focus + ,:sun_glow_size + ,:scene_gamma + ,:star_brightness + ,:cloud_color_r + ,:cloud_color_g + ,:cloud_color_b + ,:cloud_color_i + ,:cloud_x + ,:cloud_y + ,:cloud_density + ,:cloud_coverage + ,:cloud_scale + ,:cloud_detail_x + ,:cloud_detail_y + ,:cloud_detail_density + ,:cloud_scroll_x + ,:cloud_scroll_x_lock + ,:cloud_scroll_y + ,:cloud_scroll_y_lock + ,:draw_classic_clouds);"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + { + conn.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("region_id", wl.regionID.ToString())); + cmd.Parameters.Add(_Database.CreateParameter("water_color_r", wl.waterColor.X)); + cmd.Parameters.Add(_Database.CreateParameter("water_color_g", wl.waterColor.Y)); + cmd.Parameters.Add(_Database.CreateParameter("water_color_b", wl.waterColor.Z)); + cmd.Parameters.Add(_Database.CreateParameter("water_fog_density_exponent", wl.waterFogDensityExponent)); + cmd.Parameters.Add(_Database.CreateParameter("underwater_fog_modifier", wl.underwaterFogModifier)); + cmd.Parameters.Add(_Database.CreateParameter("reflection_wavelet_scale_1", wl.reflectionWaveletScale.X)); + cmd.Parameters.Add(_Database.CreateParameter("reflection_wavelet_scale_2", wl.reflectionWaveletScale.Y)); + cmd.Parameters.Add(_Database.CreateParameter("reflection_wavelet_scale_3", wl.reflectionWaveletScale.Z)); + cmd.Parameters.Add(_Database.CreateParameter("fresnel_scale", wl.fresnelScale)); + cmd.Parameters.Add(_Database.CreateParameter("fresnel_offset", wl.fresnelOffset)); + cmd.Parameters.Add(_Database.CreateParameter("refract_scale_above", wl.refractScaleAbove)); + cmd.Parameters.Add(_Database.CreateParameter("refract_scale_below", wl.refractScaleBelow)); + cmd.Parameters.Add(_Database.CreateParameter("blur_multiplier", wl.blurMultiplier)); + cmd.Parameters.Add(_Database.CreateParameter("big_wave_direction_x", wl.bigWaveDirection.X)); + cmd.Parameters.Add(_Database.CreateParameter("big_wave_direction_y", wl.bigWaveDirection.Y)); + cmd.Parameters.Add(_Database.CreateParameter("little_wave_direction_x", wl.littleWaveDirection.X)); + cmd.Parameters.Add(_Database.CreateParameter("little_wave_direction_y", wl.littleWaveDirection.Y)); + cmd.Parameters.Add(_Database.CreateParameter("normal_map_texture", wl.normalMapTexture.ToString())); + cmd.Parameters.Add(_Database.CreateParameter("horizon_r", wl.horizon.X)); + cmd.Parameters.Add(_Database.CreateParameter("horizon_g", wl.horizon.Y)); + cmd.Parameters.Add(_Database.CreateParameter("horizon_b", wl.horizon.Z)); + cmd.Parameters.Add(_Database.CreateParameter("horizon_i", wl.horizon.W)); + cmd.Parameters.Add(_Database.CreateParameter("haze_horizon", wl.hazeHorizon)); + cmd.Parameters.Add(_Database.CreateParameter("blue_density_r", wl.blueDensity.X)); + cmd.Parameters.Add(_Database.CreateParameter("blue_density_g", wl.blueDensity.Y)); + cmd.Parameters.Add(_Database.CreateParameter("blue_density_b", wl.blueDensity.Z)); + cmd.Parameters.Add(_Database.CreateParameter("blue_density_i", wl.blueDensity.W)); + cmd.Parameters.Add(_Database.CreateParameter("haze_density", wl.hazeDensity)); + cmd.Parameters.Add(_Database.CreateParameter("density_multiplier", wl.densityMultiplier)); + cmd.Parameters.Add(_Database.CreateParameter("distance_multiplier", wl.distanceMultiplier)); + cmd.Parameters.Add(_Database.CreateParameter("max_altitude", wl.maxAltitude)); + cmd.Parameters.Add(_Database.CreateParameter("sun_moon_color_r", wl.sunMoonColor.X)); + cmd.Parameters.Add(_Database.CreateParameter("sun_moon_color_g", wl.sunMoonColor.Y)); + cmd.Parameters.Add(_Database.CreateParameter("sun_moon_color_b", wl.sunMoonColor.Z)); + cmd.Parameters.Add(_Database.CreateParameter("sun_moon_color_i", wl.sunMoonColor.W)); + cmd.Parameters.Add(_Database.CreateParameter("sun_moon_position", wl.sunMoonPosition)); + cmd.Parameters.Add(_Database.CreateParameter("ambient_r", wl.ambient.X)); + cmd.Parameters.Add(_Database.CreateParameter("ambient_g", wl.ambient.Y)); + cmd.Parameters.Add(_Database.CreateParameter("ambient_b", wl.ambient.Z)); + cmd.Parameters.Add(_Database.CreateParameter("ambient_i", wl.ambient.W)); + cmd.Parameters.Add(_Database.CreateParameter("east_angle", wl.eastAngle)); + cmd.Parameters.Add(_Database.CreateParameter("sun_glow_focus", wl.sunGlowFocus)); + cmd.Parameters.Add(_Database.CreateParameter("sun_glow_size", wl.sunGlowSize)); + cmd.Parameters.Add(_Database.CreateParameter("scene_gamma", wl.sceneGamma)); + cmd.Parameters.Add(_Database.CreateParameter("star_brightness", wl.starBrightness)); + cmd.Parameters.Add(_Database.CreateParameter("cloud_color_r", wl.cloudColor.X)); + cmd.Parameters.Add(_Database.CreateParameter("cloud_color_g", wl.cloudColor.Y)); + cmd.Parameters.Add(_Database.CreateParameter("cloud_color_b", wl.cloudColor.Z)); + cmd.Parameters.Add(_Database.CreateParameter("cloud_color_i", wl.cloudColor.W)); + cmd.Parameters.Add(_Database.CreateParameter("cloud_x", wl.cloudXYDensity.X)); + cmd.Parameters.Add(_Database.CreateParameter("cloud_y", wl.cloudXYDensity.Y)); + cmd.Parameters.Add(_Database.CreateParameter("cloud_density", wl.cloudXYDensity.Z)); + cmd.Parameters.Add(_Database.CreateParameter("cloud_coverage", wl.cloudCoverage)); + cmd.Parameters.Add(_Database.CreateParameter("cloud_scale", wl.cloudScale)); + cmd.Parameters.Add(_Database.CreateParameter("cloud_detail_x", wl.cloudDetailXYDensity.X)); + cmd.Parameters.Add(_Database.CreateParameter("cloud_detail_y", wl.cloudDetailXYDensity.Y)); + cmd.Parameters.Add(_Database.CreateParameter("cloud_detail_density", wl.cloudDetailXYDensity.Z)); + cmd.Parameters.Add(_Database.CreateParameter("cloud_scroll_x", wl.cloudScrollX)); + cmd.Parameters.Add(_Database.CreateParameter("cloud_scroll_x_lock", wl.cloudScrollXLock)); + cmd.Parameters.Add(_Database.CreateParameter("cloud_scroll_y", wl.cloudScrollY)); + cmd.Parameters.Add(_Database.CreateParameter("cloud_scroll_y_lock", wl.cloudScrollYLock)); + cmd.Parameters.Add(_Database.CreateParameter("draw_classic_clouds", wl.drawClassicClouds)); + + cmd.ExecuteNonQuery(); + } + } + #region update + // } + // else + // { + // // sql update + // sql = @"UPDATE [OpenSim].[dbo].[regionwindlight] + // SET [region_id] = @region_id + // ,[water_color_r] = @water_color_r + // ,[water_color_g] = @water_color_g + // ,[water_color_b] = @water_color_b + // ,[water_fog_density_exponent] = @water_fog_density_exponent + // ,[underwater_fog_modifier] = @underwater_fog_modifier + // ,[reflection_wavelet_scale_1] = @reflection_wavelet_scale_1 + // ,[reflection_wavelet_scale_2] = @reflection_wavelet_scale_2 + // ,[reflection_wavelet_scale_3] = @reflection_wavelet_scale_3 + // ,[fresnel_scale] = @fresnel_scale + // ,[fresnel_offset] = @fresnel_offset + // ,[refract_scale_above] = @refract_scale_above + // ,[refract_scale_below] = @refract_scale_below + // ,[blur_multiplier] = @blur_multiplier + // ,[big_wave_direction_x] = @big_wave_direction_x + // ,[big_wave_direction_y] = @big_wave_direction_y + // ,[little_wave_direction_x] = @little_wave_direction_x + // ,[little_wave_direction_y] = @little_wave_direction_y + // ,[normal_map_texture] = @normal_map_texture + // ,[horizon_r] = @horizon_r + // ,[horizon_g] = @horizon_g + // ,[horizon_b] = @horizon_b + // ,[horizon_i] = @horizon_i + // ,[haze_horizon] = @haze_horizon + // ,[blue_density_r] = @blue_density_r + // ,[blue_density_g] = @blue_density_g + // ,[blue_density_b] = @blue_density_b + // ,[blue_density_i] = @blue_density_i + // ,[haze_density] = @haze_density + // ,[density_multiplier] = @density_multiplier + // ,[distance_multiplier] = @distance_multiplier + // ,[max_altitude] = @max_altitude + // ,[sun_moon_color_r] = @sun_moon_color_r + // ,[sun_moon_color_g] = @sun_moon_color_g + // ,[sun_moon_color_b] = @sun_moon_color_b + // ,[sun_moon_color_i] = @sun_moon_color_i + // ,[sun_moon_position] = @sun_moon_position + // ,[ambient_r] = @ambient_r + // ,[ambient_g] = @ambient_g + // ,[ambient_b] = @ambient_b + // ,[ambient_i] = @ambient_i + // ,[east_angle] = @east_angle + // ,[sun_glow_focus] = @sun_glow_focus + // ,[sun_glow_size] = @sun_glow_size + // ,[scene_gamma] = @scene_gamma + // ,[star_brightness] = @star_brightness + // ,[cloud_color_r] = @cloud_color_r + // ,[cloud_color_g] = @cloud_color_g + // ,[cloud_color_b] = @cloud_color_b + // ,[cloud_color_i] = @cloud_color_i + // ,[cloud_x] = @cloud_x + // ,[cloud_y] = @cloud_y + // ,[cloud_density] = @cloud_density + // ,[cloud_coverage] = @cloud_coverage + // ,[cloud_scale] = @cloud_scale + // ,[cloud_detail_x] = @cloud_detail_x + // ,[cloud_detail_y] = @cloud_detail_y + // ,[cloud_detail_density] = @cloud_detail_density + // ,[cloud_scroll_x] = @cloud_scroll_x + // ,[cloud_scroll_x_lock] = @cloud_scroll_x_lock + // ,[cloud_scroll_y] = @cloud_scroll_y + // ,[cloud_scroll_y_lock] = @cloud_scroll_y_lock + // ,[draw_classic_clouds] = @draw_classic_clouds + // WHERE region_id = @region_id"; + // using (SqlConnection conn = new SqlConnection(m_connectionString)) + // { + // conn.Open(); + // using (SqlCommand cmd = new SqlCommand(sql, conn)) + // { + // cmd.Parameters.AddWithValue("region_id", wl.regionID); + // cmd.Parameters.AddWithValue("water_color_r", wl.waterColor.X); + // cmd.Parameters.AddWithValue("water_color_g", wl.waterColor.Y); + // cmd.Parameters.AddWithValue("water_color_b", wl.waterColor.Z); + // cmd.Parameters.AddWithValue("water_fog_density_exponent", wl.waterFogDensityExponent); + // cmd.Parameters.AddWithValue("underwater_fog_modifier", wl.underwaterFogModifier); + // cmd.Parameters.AddWithValue("reflection_wavelet_scale_1", wl.reflectionWaveletScale.X); + // cmd.Parameters.AddWithValue("reflection_wavelet_scale_2", wl.reflectionWaveletScale.Y); + // cmd.Parameters.AddWithValue("reflection_wavelet_scale_3", wl.reflectionWaveletScale.Z); + // cmd.Parameters.AddWithValue("fresnel_scale", wl.fresnelScale); + // cmd.Parameters.AddWithValue("fresnel_offset", wl.fresnelOffset); + // cmd.Parameters.AddWithValue("refract_scale_above", wl.refractScaleAbove); + // cmd.Parameters.AddWithValue("refract_scale_below", wl.refractScaleBelow); + // cmd.Parameters.AddWithValue("blur_multiplier", wl.blurMultiplier); + // cmd.Parameters.AddWithValue("big_wave_direction_x", wl.bigWaveDirection.X); + // cmd.Parameters.AddWithValue("big_wave_direction_y", wl.bigWaveDirection.Y); + // cmd.Parameters.AddWithValue("little_wave_direction_x", wl.littleWaveDirection.X); + // cmd.Parameters.AddWithValue("little_wave_direction_y", wl.littleWaveDirection.Y); + // cmd.Parameters.AddWithValue("normal_map_texture", wl.normalMapTexture); + // cmd.Parameters.AddWithValue("horizon_r", wl.horizon.X); + // cmd.Parameters.AddWithValue("horizon_g", wl.horizon.Y); + // cmd.Parameters.AddWithValue("horizon_b", wl.horizon.Z); + // cmd.Parameters.AddWithValue("horizon_i", wl.horizon.W); + // cmd.Parameters.AddWithValue("haze_horizon", wl.hazeHorizon); + // cmd.Parameters.AddWithValue("blue_density_r", wl.blueDensity.X); + // cmd.Parameters.AddWithValue("blue_density_g", wl.blueDensity.Y); + // cmd.Parameters.AddWithValue("blue_density_b", wl.blueDensity.Z); + // cmd.Parameters.AddWithValue("blue_density_i", wl.blueDensity.W); + // cmd.Parameters.AddWithValue("haze_density", wl.hazeDensity); + // cmd.Parameters.AddWithValue("density_multiplier", wl.densityMultiplier); + // cmd.Parameters.AddWithValue("distance_multiplier", wl.distanceMultiplier); + // cmd.Parameters.AddWithValue("max_altitude", wl.maxAltitude); + // cmd.Parameters.AddWithValue("sun_moon_color_r", wl.sunMoonColor.X); + // cmd.Parameters.AddWithValue("sun_moon_color_g", wl.sunMoonColor.Y); + // cmd.Parameters.AddWithValue("sun_moon_color_b", wl.sunMoonColor.Z); + // cmd.Parameters.AddWithValue("sun_moon_color_i", wl.sunMoonColor.W); + // cmd.Parameters.AddWithValue("sun_moon_position", wl.sunMoonPosition); + // cmd.Parameters.AddWithValue("ambient_r", wl.ambient.X); + // cmd.Parameters.AddWithValue("ambient_g", wl.ambient.Y); + // cmd.Parameters.AddWithValue("ambient_b", wl.ambient.Z); + // cmd.Parameters.AddWithValue("ambient_i", wl.ambient.W); + // cmd.Parameters.AddWithValue("east_angle", wl.eastAngle); + // cmd.Parameters.AddWithValue("sun_glow_focus", wl.sunGlowFocus); + // cmd.Parameters.AddWithValue("sun_glow_size", wl.sunGlowSize); + // cmd.Parameters.AddWithValue("scene_gamma", wl.sceneGamma); + // cmd.Parameters.AddWithValue("star_brightness", wl.starBrightness); + // cmd.Parameters.AddWithValue("cloud_color_r", wl.cloudColor.X); + // cmd.Parameters.AddWithValue("cloud_color_g", wl.cloudColor.Y); + // cmd.Parameters.AddWithValue("cloud_color_b", wl.cloudColor.Z); + // cmd.Parameters.AddWithValue("cloud_color_i", wl.cloudColor.W); + // cmd.Parameters.AddWithValue("cloud_x", wl.cloudXYDensity.X); + // cmd.Parameters.AddWithValue("cloud_y", wl.cloudXYDensity.Y); + // cmd.Parameters.AddWithValue("cloud_density", wl.cloudXYDensity.Z); + // cmd.Parameters.AddWithValue("cloud_coverage", wl.cloudCoverage); + // cmd.Parameters.AddWithValue("cloud_scale", wl.cloudScale); + // cmd.Parameters.AddWithValue("cloud_detail_x", wl.cloudDetailXYDensity.X); + // cmd.Parameters.AddWithValue("cloud_detail_y", wl.cloudDetailXYDensity.Y); + // cmd.Parameters.AddWithValue("cloud_detail_density", wl.cloudDetailXYDensity.Z); + // cmd.Parameters.AddWithValue("cloud_scroll_x", wl.cloudScrollX); + // cmd.Parameters.AddWithValue("cloud_scroll_x_lock", wl.cloudScrollXLock); + // cmd.Parameters.AddWithValue("cloud_scroll_y", wl.cloudScrollY); + // cmd.Parameters.AddWithValue("cloud_scroll_y_lock", wl.cloudScrollYLock); + // cmd.Parameters.AddWithValue("draw_classic_clouds", wl.drawClassicClouds); + + // cmd.ExecuteNonQuery(); + // } + // } + // } + #endregion + } + + #region Environment Settings + public string LoadRegionEnvironmentSettings(UUID regionUUID) + { + string sql = "select * from regionenvironment where region_id = :region_id"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("region_id", regionUUID)); + conn.Open(); + using (NpgsqlDataReader result = cmd.ExecuteReader()) + { + if (!result.Read()) + { + return String.Empty; + } + else + { + return Convert.ToString(result["llsd_settings"]); + } + } + } + } + + public void StoreRegionEnvironmentSettings(UUID regionUUID, string settings) + { + { + string sql = "DELETE FROM regionenvironment WHERE region_id = :region_id ;"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("region_id", regionUUID)); + conn.Open(); + cmd.ExecuteNonQuery(); + } + + sql = "INSERT INTO regionenvironment (region_id, llsd_settings) VALUES (:region_id, :llsd_settings) ;"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("region_id", regionUUID)); + cmd.Parameters.Add(_Database.CreateParameter("llsd_settings", settings)); + + conn.Open(); + cmd.ExecuteNonQuery(); + } + } + } + + public void RemoveRegionEnvironmentSettings(UUID regionUUID) + { + string sql = "delete from regionenvironment where region_id = :region_id ;"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("region_id", regionUUID)); + + conn.Open(); + cmd.ExecuteNonQuery(); + } + } + #endregion + + /// + /// Loads the settings of a region. + /// + /// The region UUID. + /// + public RegionSettings LoadRegionSettings(UUID regionUUID) + { + string sql = @"select * from regionsettings where ""regionUUID"" = :regionUUID"; + RegionSettings regionSettings; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("regionUUID", regionUUID)); + conn.Open(); + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + if (reader.Read()) + { + regionSettings = BuildRegionSettings(reader); + regionSettings.OnSave += StoreRegionSettings; + + return regionSettings; + } + } + } + + //If we reach this point then there are new region settings for that region + regionSettings = new RegionSettings(); + regionSettings.RegionUUID = regionUUID; + regionSettings.OnSave += StoreRegionSettings; + + //Store new values + StoreNewRegionSettings(regionSettings); + + LoadSpawnPoints(regionSettings); + + return regionSettings; + } + + /// + /// Store region settings, need to check if the check is really necesary. If we can make something for creating new region. + /// + /// region settings. + public void StoreRegionSettings(RegionSettings regionSettings) + { + //Little check if regionUUID already exist in DB + string regionUUID; + string sql = @"SELECT ""regionUUID"" FROM regionsettings WHERE ""regionUUID"" = :regionUUID"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("regionUUID", regionSettings.RegionUUID)); + conn.Open(); + regionUUID = cmd.ExecuteScalar().ToString(); + } + + if (string.IsNullOrEmpty(regionUUID)) + { + StoreNewRegionSettings(regionSettings); + } + else + { + //This method only updates region settings!!! First call LoadRegionSettings to create new region settings in DB + sql = + @"UPDATE regionsettings SET block_terraform = :block_terraform ,block_fly = :block_fly ,allow_damage = :allow_damage +,restrict_pushing = :restrict_pushing ,allow_land_resell = :allow_land_resell ,allow_land_join_divide = :allow_land_join_divide +,block_show_in_search = :block_show_in_search ,agent_limit = :agent_limit ,object_bonus = :object_bonus ,maturity = :maturity +,disable_scripts = :disable_scripts ,disable_collisions = :disable_collisions ,disable_physics = :disable_physics +,terrain_texture_1 = :terrain_texture_1 ,terrain_texture_2 = :terrain_texture_2 ,terrain_texture_3 = :terrain_texture_3 +,terrain_texture_4 = :terrain_texture_4 ,elevation_1_nw = :elevation_1_nw ,elevation_2_nw = :elevation_2_nw +,elevation_1_ne = :elevation_1_ne ,elevation_2_ne = :elevation_2_ne ,elevation_1_se = :elevation_1_se ,elevation_2_se = :elevation_2_se +,elevation_1_sw = :elevation_1_sw ,elevation_2_sw = :elevation_2_sw ,water_height = :water_height ,terrain_raise_limit = :terrain_raise_limit +,terrain_lower_limit = :terrain_lower_limit ,use_estate_sun = :use_estate_sun ,fixed_sun = :fixed_sun ,sun_position = :sun_position +,covenant = :covenant ,covenant_datetime = :covenant_datetime, sunvectorx = :sunvectorx, sunvectory = :sunvectory, sunvectorz = :sunvectorz, +""Sandbox"" = :Sandbox, loaded_creation_datetime = :loaded_creation_datetime, loaded_creation_id = :loaded_creation_id, ""map_tile_ID"" = :TerrainImageID, +""TelehubObject"" = :telehubobject, ""parcel_tile_ID"" = :ParcelImageID + WHERE ""regionUUID"" = :regionUUID"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.AddRange(CreateRegionSettingParameters(regionSettings)); + conn.Open(); + cmd.ExecuteNonQuery(); + } + } + SaveSpawnPoints(regionSettings); + } + + public void Shutdown() + { + //Not used?? + } + + #region Private Methods + + /// + /// Serializes the terrain data for storage in DB. + /// + /// terrain data + /// + private static Array serializeTerrain(double[,] val) + { + MemoryStream str = new MemoryStream(((int)Constants.RegionSize * (int)Constants.RegionSize) * sizeof(double)); + BinaryWriter bw = new BinaryWriter(str); + + // TODO: COMPATIBILITY - Add byte-order conversions + for (int x = 0; x < (int)Constants.RegionSize; x++) + for (int y = 0; y < (int)Constants.RegionSize; y++) + { + double height = val[x, y]; + if (height == 0.0) + height = double.Epsilon; + + bw.Write(height); + } + + return str.ToArray(); + } + + /// + /// Stores new regionsettings. + /// + /// The region settings. + private void StoreNewRegionSettings(RegionSettings regionSettings) + { + string sql = @"INSERT INTO regionsettings + (""regionUUID"",block_terraform,block_fly,allow_damage,restrict_pushing,allow_land_resell,allow_land_join_divide, + block_show_in_search,agent_limit,object_bonus,maturity,disable_scripts,disable_collisions,disable_physics, + terrain_texture_1,terrain_texture_2,terrain_texture_3,terrain_texture_4,elevation_1_nw,elevation_2_nw,elevation_1_ne, + elevation_2_ne,elevation_1_se,elevation_2_se,elevation_1_sw,elevation_2_sw,water_height,terrain_raise_limit, + terrain_lower_limit,use_estate_sun,fixed_sun,sun_position,covenant,covenant_datetime,sunvectorx, sunvectory, sunvectorz, + ""Sandbox"", loaded_creation_datetime, loaded_creation_id + ) + VALUES + (:regionUUID,:block_terraform,:block_fly,:allow_damage,:restrict_pushing,:allow_land_resell,:allow_land_join_divide, + :block_show_in_search,:agent_limit,:object_bonus,:maturity,:disable_scripts,:disable_collisions,:disable_physics, + :terrain_texture_1,:terrain_texture_2,:terrain_texture_3,:terrain_texture_4,:elevation_1_nw,:elevation_2_nw,:elevation_1_ne, + :elevation_2_ne,:elevation_1_se,:elevation_2_se,:elevation_1_sw,:elevation_2_sw,:water_height,:terrain_raise_limit, + :terrain_lower_limit,:use_estate_sun,:fixed_sun,:sun_position,:covenant, :covenant_datetime, :sunvectorx,:sunvectory, + :sunvectorz, :Sandbox, :loaded_creation_datetime, :loaded_creation_id )"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.AddRange(CreateRegionSettingParameters(regionSettings)); + conn.Open(); + cmd.ExecuteNonQuery(); + } + } + + #region Private DataRecord conversion methods + + /// + /// Builds the region settings from a datarecod. + /// + /// datarecord with regionsettings. + /// + private static RegionSettings BuildRegionSettings(IDataRecord row) + { + //TODO change this is some more generic code so we doesnt have to change it every time a new field is added? + RegionSettings newSettings = new RegionSettings(); + + newSettings.RegionUUID = new UUID((Guid)row["regionUUID"]); + newSettings.BlockTerraform = Convert.ToBoolean(row["block_terraform"]); + newSettings.AllowDamage = Convert.ToBoolean(row["allow_damage"]); + newSettings.BlockFly = Convert.ToBoolean(row["block_fly"]); + newSettings.RestrictPushing = Convert.ToBoolean(row["restrict_pushing"]); + newSettings.AllowLandResell = Convert.ToBoolean(row["allow_land_resell"]); + newSettings.AllowLandJoinDivide = Convert.ToBoolean(row["allow_land_join_divide"]); + newSettings.BlockShowInSearch = Convert.ToBoolean(row["block_show_in_search"]); + newSettings.AgentLimit = Convert.ToInt32(row["agent_limit"]); + newSettings.ObjectBonus = Convert.ToDouble(row["object_bonus"]); + newSettings.Maturity = Convert.ToInt32(row["maturity"]); + newSettings.DisableScripts = Convert.ToBoolean(row["disable_scripts"]); + newSettings.DisableCollisions = Convert.ToBoolean(row["disable_collisions"]); + newSettings.DisablePhysics = Convert.ToBoolean(row["disable_physics"]); + newSettings.TerrainTexture1 = new UUID((Guid)row["terrain_texture_1"]); + newSettings.TerrainTexture2 = new UUID((Guid)row["terrain_texture_2"]); + newSettings.TerrainTexture3 = new UUID((Guid)row["terrain_texture_3"]); + newSettings.TerrainTexture4 = new UUID((Guid)row["terrain_texture_4"]); + newSettings.Elevation1NW = Convert.ToDouble(row["elevation_1_nw"]); + newSettings.Elevation2NW = Convert.ToDouble(row["elevation_2_nw"]); + newSettings.Elevation1NE = Convert.ToDouble(row["elevation_1_ne"]); + newSettings.Elevation2NE = Convert.ToDouble(row["elevation_2_ne"]); + newSettings.Elevation1SE = Convert.ToDouble(row["elevation_1_se"]); + newSettings.Elevation2SE = Convert.ToDouble(row["elevation_2_se"]); + newSettings.Elevation1SW = Convert.ToDouble(row["elevation_1_sw"]); + newSettings.Elevation2SW = Convert.ToDouble(row["elevation_2_sw"]); + newSettings.WaterHeight = Convert.ToDouble(row["water_height"]); + newSettings.TerrainRaiseLimit = Convert.ToDouble(row["terrain_raise_limit"]); + newSettings.TerrainLowerLimit = Convert.ToDouble(row["terrain_lower_limit"]); + newSettings.UseEstateSun = Convert.ToBoolean(row["use_estate_sun"]); + newSettings.Sandbox = Convert.ToBoolean(row["Sandbox"]); + newSettings.FixedSun = Convert.ToBoolean(row["fixed_sun"]); + newSettings.SunPosition = Convert.ToDouble(row["sun_position"]); + newSettings.SunVector = new Vector3( + Convert.ToSingle(row["sunvectorx"]), + Convert.ToSingle(row["sunvectory"]), + Convert.ToSingle(row["sunvectorz"]) + ); + newSettings.Covenant = new UUID((Guid)row["covenant"]); + newSettings.CovenantChangedDateTime = Convert.ToInt32(row["covenant_datetime"]); + newSettings.LoadedCreationDateTime = Convert.ToInt32(row["loaded_creation_datetime"]); + + if (row["loaded_creation_id"] is DBNull) + newSettings.LoadedCreationID = ""; + else + newSettings.LoadedCreationID = (String)row["loaded_creation_id"]; + + newSettings.TerrainImageID = new UUID((string)row["map_tile_ID"]); + newSettings.ParcelImageID = new UUID((Guid)row["parcel_tile_ID"]); + newSettings.TelehubObject = new UUID((Guid)row["TelehubObject"]); + + return newSettings; + } + + /// + /// Builds the land data from a datarecord. + /// + /// datarecord with land data + /// + private static LandData BuildLandData(IDataRecord row) + { + LandData newData = new LandData(); + + newData.GlobalID = new UUID((Guid)row["UUID"]); + newData.LocalID = Convert.ToInt32(row["LocalLandID"]); + + // Bitmap is a byte[512] + newData.Bitmap = (Byte[])row["Bitmap"]; + + newData.Name = (string)row["Name"]; + newData.Description = (string)row["Description"]; + newData.OwnerID = new UUID((Guid)row["OwnerUUID"]); + newData.IsGroupOwned = Convert.ToBoolean(row["IsGroupOwned"]); + newData.Area = Convert.ToInt32(row["Area"]); + newData.AuctionID = Convert.ToUInt32(row["AuctionID"]); //Unemplemented + newData.Category = (ParcelCategory)Convert.ToInt32(row["Category"]); + //Enum libsecondlife.Parcel.ParcelCategory + newData.ClaimDate = Convert.ToInt32(row["ClaimDate"]); + newData.ClaimPrice = Convert.ToInt32(row["ClaimPrice"]); + newData.GroupID = new UUID((Guid)row["GroupUUID"]); + newData.SalePrice = Convert.ToInt32(row["SalePrice"]); + newData.Status = (ParcelStatus)Convert.ToInt32(row["LandStatus"]); + //Enum. libsecondlife.Parcel.ParcelStatus + newData.Flags = Convert.ToUInt32(row["LandFlags"]); + newData.LandingType = Convert.ToByte(row["LandingType"]); + newData.MediaAutoScale = Convert.ToByte(row["MediaAutoScale"]); + newData.MediaID = new UUID((Guid)row["MediaTextureUUID"]); + newData.MediaURL = (string)row["MediaURL"]; + newData.MusicURL = (string)row["MusicURL"]; + newData.PassHours = Convert.ToSingle(row["PassHours"]); + newData.PassPrice = Convert.ToInt32(row["PassPrice"]); + + // UUID authedbuyer; + // UUID snapshotID; + // + // if (UUID.TryParse((string)row["AuthBuyerID"], out authedbuyer)) + // newData.AuthBuyerID = authedbuyer; + // + // if (UUID.TryParse((string)row["SnapshotUUID"], out snapshotID)) + // newData.SnapshotID = snapshotID; + newData.AuthBuyerID = new UUID((Guid)row["AuthBuyerID"]); + newData.SnapshotID = new UUID((Guid)row["SnapshotUUID"]); + + newData.OtherCleanTime = Convert.ToInt32(row["OtherCleanTime"]); + + try + { + newData.UserLocation = + new Vector3(Convert.ToSingle(row["UserLocationX"]), Convert.ToSingle(row["UserLocationY"]), + Convert.ToSingle(row["UserLocationZ"])); + newData.UserLookAt = + new Vector3(Convert.ToSingle(row["UserLookAtX"]), Convert.ToSingle(row["UserLookAtY"]), + Convert.ToSingle(row["UserLookAtZ"])); + } + catch (InvalidCastException) + { + newData.UserLocation = Vector3.Zero; + newData.UserLookAt = Vector3.Zero; + _Log.ErrorFormat("[PARCEL]: unable to get parcel telehub settings for {1}", newData.Name); + } + + newData.ParcelAccessList = new List(); + newData.MediaDescription = (string)row["MediaDescription"]; + newData.MediaType = (string)row["MediaType"]; + newData.MediaWidth = Convert.ToInt32((((string)row["MediaSize"]).Split(','))[0]); + newData.MediaHeight = Convert.ToInt32((((string)row["MediaSize"]).Split(','))[1]); + newData.MediaLoop = Convert.ToBoolean(row["MediaLoop"]); + newData.ObscureMusic = Convert.ToBoolean(row["ObscureMusic"]); + newData.ObscureMedia = Convert.ToBoolean(row["ObscureMedia"]); + + return newData; + } + + /// + /// Builds the landaccess data from a data record. + /// + /// datarecord with landaccess data + /// + private static LandAccessEntry BuildLandAccessData(IDataRecord row) + { + LandAccessEntry entry = new LandAccessEntry(); + entry.AgentID = new UUID((Guid)row["AccessUUID"]); + entry.Flags = (AccessList)Convert.ToInt32(row["Flags"]); + entry.Expires = Convert.ToInt32(row["Expires"]); + return entry; + } + + /// + /// Builds the prim from a datarecord. + /// + /// datarecord + /// + private static SceneObjectPart BuildPrim(IDataRecord primRow) + { + SceneObjectPart prim = new SceneObjectPart(); + + prim.UUID = new UUID((Guid)primRow["UUID"]); + // explicit conversion of integers is required, which sort + // of sucks. No idea if there is a shortcut here or not. + prim.CreationDate = Convert.ToInt32(primRow["CreationDate"]); + prim.Name = (string)primRow["Name"]; + // various text fields + prim.Text = (string)primRow["Text"]; + prim.Color = Color.FromArgb(Convert.ToInt32(primRow["ColorA"]), + Convert.ToInt32(primRow["ColorR"]), + Convert.ToInt32(primRow["ColorG"]), + Convert.ToInt32(primRow["ColorB"])); + prim.Description = (string)primRow["Description"]; + prim.SitName = (string)primRow["SitName"]; + prim.TouchName = (string)primRow["TouchName"]; + // permissions + prim.Flags = (PrimFlags)Convert.ToUInt32(primRow["ObjectFlags"]); + //prim.creatorID = new UUID((Guid)primRow["creatorID"]); + prim.CreatorIdentification = (string)primRow["CreatorID"].ToString(); + prim.OwnerID = new UUID((Guid)primRow["OwnerID"]); + prim.GroupID = new UUID((Guid)primRow["GroupID"]); + prim.LastOwnerID = new UUID((Guid)primRow["LastOwnerID"]); + prim.OwnerMask = Convert.ToUInt32(primRow["OwnerMask"]); + prim.NextOwnerMask = Convert.ToUInt32(primRow["NextOwnerMask"]); + prim.GroupMask = Convert.ToUInt32(primRow["GroupMask"]); + prim.EveryoneMask = Convert.ToUInt32(primRow["EveryoneMask"]); + prim.BaseMask = Convert.ToUInt32(primRow["BaseMask"]); + // vectors + prim.OffsetPosition = new Vector3( + Convert.ToSingle(primRow["PositionX"]), + Convert.ToSingle(primRow["PositionY"]), + Convert.ToSingle(primRow["PositionZ"])); + + prim.GroupPosition = new Vector3( + Convert.ToSingle(primRow["GroupPositionX"]), + Convert.ToSingle(primRow["GroupPositionY"]), + Convert.ToSingle(primRow["GroupPositionZ"])); + + prim.Velocity = new Vector3( + Convert.ToSingle(primRow["VelocityX"]), + Convert.ToSingle(primRow["VelocityY"]), + Convert.ToSingle(primRow["VelocityZ"])); + + prim.AngularVelocity = new Vector3( + Convert.ToSingle(primRow["AngularVelocityX"]), + Convert.ToSingle(primRow["AngularVelocityY"]), + Convert.ToSingle(primRow["AngularVelocityZ"])); + + prim.Acceleration = new Vector3( + Convert.ToSingle(primRow["AccelerationX"]), + Convert.ToSingle(primRow["AccelerationY"]), + Convert.ToSingle(primRow["AccelerationZ"])); + + // quaternions + prim.RotationOffset = new Quaternion( + Convert.ToSingle(primRow["RotationX"]), + Convert.ToSingle(primRow["RotationY"]), + Convert.ToSingle(primRow["RotationZ"]), + Convert.ToSingle(primRow["RotationW"])); + + prim.SitTargetPositionLL = new Vector3( + Convert.ToSingle(primRow["SitTargetOffsetX"]), + Convert.ToSingle(primRow["SitTargetOffsetY"]), + Convert.ToSingle(primRow["SitTargetOffsetZ"])); + + prim.SitTargetOrientationLL = new Quaternion( + Convert.ToSingle(primRow["SitTargetOrientX"]), + Convert.ToSingle(primRow["SitTargetOrientY"]), + Convert.ToSingle(primRow["SitTargetOrientZ"]), + Convert.ToSingle(primRow["SitTargetOrientW"])); + + prim.PayPrice[0] = Convert.ToInt32(primRow["PayPrice"]); + prim.PayPrice[1] = Convert.ToInt32(primRow["PayButton1"]); + prim.PayPrice[2] = Convert.ToInt32(primRow["PayButton2"]); + prim.PayPrice[3] = Convert.ToInt32(primRow["PayButton3"]); + prim.PayPrice[4] = Convert.ToInt32(primRow["PayButton4"]); + + prim.Sound = new UUID((Guid)primRow["LoopedSound"]); + prim.SoundGain = Convert.ToSingle(primRow["LoopedSoundGain"]); + prim.SoundFlags = 1; // If it's persisted at all, it's looped + + if (!(primRow["TextureAnimation"] is DBNull)) + prim.TextureAnimation = (Byte[])primRow["TextureAnimation"]; + if (!(primRow["ParticleSystem"] is DBNull)) + prim.ParticleSystem = (Byte[])primRow["ParticleSystem"]; + + prim.AngularVelocity = new Vector3( + Convert.ToSingle(primRow["OmegaX"]), + Convert.ToSingle(primRow["OmegaY"]), + Convert.ToSingle(primRow["OmegaZ"])); + + prim.SetCameraEyeOffset(new Vector3( + Convert.ToSingle(primRow["CameraEyeOffsetX"]), + Convert.ToSingle(primRow["CameraEyeOffsetY"]), + Convert.ToSingle(primRow["CameraEyeOffsetZ"]) + )); + + prim.SetCameraAtOffset(new Vector3( + Convert.ToSingle(primRow["CameraAtOffsetX"]), + Convert.ToSingle(primRow["CameraAtOffsetY"]), + Convert.ToSingle(primRow["CameraAtOffsetZ"]) + )); + + if (Convert.ToInt16(primRow["ForceMouselook"]) != 0) + prim.SetForceMouselook(true); + + prim.ScriptAccessPin = Convert.ToInt32(primRow["ScriptAccessPin"]); + + if (Convert.ToInt16(primRow["AllowedDrop"]) != 0) + prim.AllowedDrop = true; + + if (Convert.ToInt16(primRow["DieAtEdge"]) != 0) + prim.DIE_AT_EDGE = true; + + prim.SalePrice = Convert.ToInt32(primRow["SalePrice"]); + prim.ObjectSaleType = Convert.ToByte(primRow["SaleType"]); + + prim.Material = Convert.ToByte(primRow["Material"]); + + if (!(primRow["ClickAction"] is DBNull)) + prim.ClickAction = Convert.ToByte(primRow["ClickAction"]); + + prim.CollisionSound = new UUID((Guid)primRow["CollisionSound"]); + prim.CollisionSoundVolume = Convert.ToSingle(primRow["CollisionSoundVolume"]); + + prim.PassTouches = (bool)primRow["PassTouches"]; + + if (!(primRow["MediaURL"] is System.DBNull)) + prim.MediaUrl = (string)primRow["MediaURL"]; + + if (!(primRow["DynAttrs"] is System.DBNull) && (string)primRow["DynAttrs"] != "") + prim.DynAttrs = DAMap.FromXml((string)primRow["DynAttrs"]); + else + prim.DynAttrs = new DAMap(); + + prim.PhysicsShapeType = Convert.ToByte(primRow["PhysicsShapeType"]); + prim.Density = Convert.ToSingle(primRow["Density"]); + prim.GravityModifier = Convert.ToSingle(primRow["GravityModifier"]); + prim.Friction = Convert.ToSingle(primRow["Friction"]); + prim.Restitution = Convert.ToSingle(primRow["Restitution"]); + + return prim; + } + + /// + /// Builds the prim shape from a datarecord. + /// + /// The row. + /// + private static PrimitiveBaseShape BuildShape(IDataRecord shapeRow) + { + PrimitiveBaseShape baseShape = new PrimitiveBaseShape(); + + baseShape.Scale = new Vector3( + (float)Convert.ToDouble(shapeRow["ScaleX"]), + (float)Convert.ToDouble(shapeRow["ScaleY"]), + (float)Convert.ToDouble(shapeRow["ScaleZ"])); + + // paths + baseShape.PCode = Convert.ToByte(shapeRow["PCode"]); + baseShape.PathBegin = Convert.ToUInt16(shapeRow["PathBegin"]); + baseShape.PathEnd = Convert.ToUInt16(shapeRow["PathEnd"]); + baseShape.PathScaleX = Convert.ToByte(shapeRow["PathScaleX"]); + baseShape.PathScaleY = Convert.ToByte(shapeRow["PathScaleY"]); + baseShape.PathShearX = Convert.ToByte(shapeRow["PathShearX"]); + baseShape.PathShearY = Convert.ToByte(shapeRow["PathShearY"]); + baseShape.PathSkew = Convert.ToSByte(shapeRow["PathSkew"]); + baseShape.PathCurve = Convert.ToByte(shapeRow["PathCurve"]); + baseShape.PathRadiusOffset = Convert.ToSByte(shapeRow["PathRadiusOffset"]); + baseShape.PathRevolutions = Convert.ToByte(shapeRow["PathRevolutions"]); + baseShape.PathTaperX = Convert.ToSByte(shapeRow["PathTaperX"]); + baseShape.PathTaperY = Convert.ToSByte(shapeRow["PathTaperY"]); + baseShape.PathTwist = Convert.ToSByte(shapeRow["PathTwist"]); + baseShape.PathTwistBegin = Convert.ToSByte(shapeRow["PathTwistBegin"]); + // profile + baseShape.ProfileBegin = Convert.ToUInt16(shapeRow["ProfileBegin"]); + baseShape.ProfileEnd = Convert.ToUInt16(shapeRow["ProfileEnd"]); + baseShape.ProfileCurve = Convert.ToByte(shapeRow["ProfileCurve"]); + baseShape.ProfileHollow = Convert.ToUInt16(shapeRow["ProfileHollow"]); + + byte[] textureEntry = (byte[])shapeRow["Texture"]; + baseShape.TextureEntry = textureEntry; + + baseShape.ExtraParams = (byte[])shapeRow["ExtraParams"]; + + try + { + baseShape.State = Convert.ToByte(shapeRow["State"]); + } + catch (InvalidCastException) + { + } + + if (!(shapeRow["Media"] is System.DBNull)) + { + baseShape.Media = PrimitiveBaseShape.MediaList.FromXml((string)shapeRow["Media"]); + } + + return baseShape; + } + + /// + /// Build a prim inventory item from the persisted data. + /// + /// + /// + private static TaskInventoryItem BuildItem(IDataRecord inventoryRow) + { + TaskInventoryItem taskItem = new TaskInventoryItem(); + + taskItem.ItemID = new UUID((Guid)inventoryRow["itemID"]); + taskItem.ParentPartID = new UUID((Guid)inventoryRow["primID"]); + taskItem.AssetID = new UUID((Guid)inventoryRow["assetID"]); + taskItem.ParentID = new UUID((Guid)inventoryRow["parentFolderID"]); + + taskItem.InvType = Convert.ToInt32(inventoryRow["invType"]); + taskItem.Type = Convert.ToInt32(inventoryRow["assetType"]); + + taskItem.Name = (string)inventoryRow["name"]; + taskItem.Description = (string)inventoryRow["description"]; + taskItem.CreationDate = Convert.ToUInt32(inventoryRow["creationDate"]); + //taskItem.creatorID = new UUID((Guid)inventoryRow["creatorID"]); + taskItem.CreatorIdentification = (string)inventoryRow["creatorID"].ToString(); + taskItem.OwnerID = new UUID((Guid)inventoryRow["ownerID"]); + taskItem.LastOwnerID = new UUID((Guid)inventoryRow["lastOwnerID"]); + taskItem.GroupID = new UUID((Guid)inventoryRow["groupID"]); + + taskItem.NextPermissions = Convert.ToUInt32(inventoryRow["nextPermissions"]); + taskItem.CurrentPermissions = Convert.ToUInt32(inventoryRow["currentPermissions"]); + taskItem.BasePermissions = Convert.ToUInt32(inventoryRow["basePermissions"]); + taskItem.EveryonePermissions = Convert.ToUInt32(inventoryRow["everyonePermissions"]); + taskItem.GroupPermissions = Convert.ToUInt32(inventoryRow["groupPermissions"]); + taskItem.Flags = Convert.ToUInt32(inventoryRow["flags"]); + + return taskItem; + } + + #endregion + + #region Create parameters methods + + /// + /// Creates the prim inventory parameters. + /// + /// item in inventory. + /// + private NpgsqlParameter[] CreatePrimInventoryParameters(TaskInventoryItem taskItem) + { + List parameters = new List(); + + parameters.Add(_Database.CreateParameter("itemID", taskItem.ItemID)); + parameters.Add(_Database.CreateParameter("primID", taskItem.ParentPartID)); + parameters.Add(_Database.CreateParameter("assetID", taskItem.AssetID)); + parameters.Add(_Database.CreateParameter("parentFolderID", taskItem.ParentID)); + parameters.Add(_Database.CreateParameter("invType", taskItem.InvType)); + parameters.Add(_Database.CreateParameter("assetType", taskItem.Type)); + + parameters.Add(_Database.CreateParameter("name", taskItem.Name)); + parameters.Add(_Database.CreateParameter("description", taskItem.Description)); + parameters.Add(_Database.CreateParameter("creationDate", taskItem.CreationDate)); + parameters.Add(_Database.CreateParameter("creatorID", taskItem.CreatorID)); + parameters.Add(_Database.CreateParameter("ownerID", taskItem.OwnerID)); + parameters.Add(_Database.CreateParameter("lastOwnerID", taskItem.LastOwnerID)); + parameters.Add(_Database.CreateParameter("groupID", taskItem.GroupID)); + parameters.Add(_Database.CreateParameter("nextPermissions", taskItem.NextPermissions)); + parameters.Add(_Database.CreateParameter("currentPermissions", taskItem.CurrentPermissions)); + parameters.Add(_Database.CreateParameter("basePermissions", taskItem.BasePermissions)); + parameters.Add(_Database.CreateParameter("everyonePermissions", taskItem.EveryonePermissions)); + parameters.Add(_Database.CreateParameter("groupPermissions", taskItem.GroupPermissions)); + parameters.Add(_Database.CreateParameter("flags", taskItem.Flags)); + + return parameters.ToArray(); + } + + /// + /// Creates the region setting parameters. + /// + /// regionsettings. + /// + private NpgsqlParameter[] CreateRegionSettingParameters(RegionSettings settings) + { + List parameters = new List(); + + parameters.Add(_Database.CreateParameter("regionUUID", settings.RegionUUID)); + parameters.Add(_Database.CreateParameter("block_terraform", settings.BlockTerraform)); + parameters.Add(_Database.CreateParameter("block_fly", settings.BlockFly)); + parameters.Add(_Database.CreateParameter("allow_damage", settings.AllowDamage)); + parameters.Add(_Database.CreateParameter("restrict_pushing", settings.RestrictPushing)); + parameters.Add(_Database.CreateParameter("allow_land_resell", settings.AllowLandResell)); + parameters.Add(_Database.CreateParameter("allow_land_join_divide", settings.AllowLandJoinDivide)); + parameters.Add(_Database.CreateParameter("block_show_in_search", settings.BlockShowInSearch)); + parameters.Add(_Database.CreateParameter("agent_limit", settings.AgentLimit)); + parameters.Add(_Database.CreateParameter("object_bonus", settings.ObjectBonus)); + parameters.Add(_Database.CreateParameter("maturity", settings.Maturity)); + parameters.Add(_Database.CreateParameter("disable_scripts", settings.DisableScripts)); + parameters.Add(_Database.CreateParameter("disable_collisions", settings.DisableCollisions)); + parameters.Add(_Database.CreateParameter("disable_physics", settings.DisablePhysics)); + parameters.Add(_Database.CreateParameter("terrain_texture_1", settings.TerrainTexture1)); + parameters.Add(_Database.CreateParameter("terrain_texture_2", settings.TerrainTexture2)); + parameters.Add(_Database.CreateParameter("terrain_texture_3", settings.TerrainTexture3)); + parameters.Add(_Database.CreateParameter("terrain_texture_4", settings.TerrainTexture4)); + parameters.Add(_Database.CreateParameter("elevation_1_nw", settings.Elevation1NW)); + parameters.Add(_Database.CreateParameter("elevation_2_nw", settings.Elevation2NW)); + parameters.Add(_Database.CreateParameter("elevation_1_ne", settings.Elevation1NE)); + parameters.Add(_Database.CreateParameter("elevation_2_ne", settings.Elevation2NE)); + parameters.Add(_Database.CreateParameter("elevation_1_se", settings.Elevation1SE)); + parameters.Add(_Database.CreateParameter("elevation_2_se", settings.Elevation2SE)); + parameters.Add(_Database.CreateParameter("elevation_1_sw", settings.Elevation1SW)); + parameters.Add(_Database.CreateParameter("elevation_2_sw", settings.Elevation2SW)); + parameters.Add(_Database.CreateParameter("water_height", settings.WaterHeight)); + parameters.Add(_Database.CreateParameter("terrain_raise_limit", settings.TerrainRaiseLimit)); + parameters.Add(_Database.CreateParameter("terrain_lower_limit", settings.TerrainLowerLimit)); + parameters.Add(_Database.CreateParameter("use_estate_sun", settings.UseEstateSun)); + parameters.Add(_Database.CreateParameter("Sandbox", settings.Sandbox)); + parameters.Add(_Database.CreateParameter("fixed_sun", settings.FixedSun)); + parameters.Add(_Database.CreateParameter("sun_position", settings.SunPosition)); + parameters.Add(_Database.CreateParameter("sunvectorx", settings.SunVector.X)); + parameters.Add(_Database.CreateParameter("sunvectory", settings.SunVector.Y)); + parameters.Add(_Database.CreateParameter("sunvectorz", settings.SunVector.Z)); + parameters.Add(_Database.CreateParameter("covenant", settings.Covenant)); + parameters.Add(_Database.CreateParameter("covenant_datetime", settings.CovenantChangedDateTime)); + parameters.Add(_Database.CreateParameter("Loaded_Creation_DateTime", settings.LoadedCreationDateTime)); + parameters.Add(_Database.CreateParameter("Loaded_Creation_ID", settings.LoadedCreationID)); + parameters.Add(_Database.CreateParameter("TerrainImageID", settings.TerrainImageID)); + parameters.Add(_Database.CreateParameter("ParcelImageID", settings.ParcelImageID)); + parameters.Add(_Database.CreateParameter("TelehubObject", settings.TelehubObject)); + + return parameters.ToArray(); + } + + /// + /// Creates the land parameters. + /// + /// land parameters. + /// region UUID. + /// + private NpgsqlParameter[] CreateLandParameters(LandData land, UUID regionUUID) + { + List parameters = new List(); + + parameters.Add(_Database.CreateParameter("UUID", land.GlobalID)); + parameters.Add(_Database.CreateParameter("RegionUUID", regionUUID)); + parameters.Add(_Database.CreateParameter("LocalLandID", land.LocalID)); + + // Bitmap is a byte[512] + parameters.Add(_Database.CreateParameter("Bitmap", land.Bitmap)); + + parameters.Add(_Database.CreateParameter("Name", land.Name)); + parameters.Add(_Database.CreateParameter("Description", land.Description)); + parameters.Add(_Database.CreateParameter("OwnerUUID", land.OwnerID)); + parameters.Add(_Database.CreateParameter("IsGroupOwned", land.IsGroupOwned)); + parameters.Add(_Database.CreateParameter("Area", land.Area)); + parameters.Add(_Database.CreateParameter("AuctionID", land.AuctionID)); //Unemplemented + parameters.Add(_Database.CreateParameter("Category", (int)land.Category)); //Enum libsecondlife.Parcel.ParcelCategory + parameters.Add(_Database.CreateParameter("ClaimDate", land.ClaimDate)); + parameters.Add(_Database.CreateParameter("ClaimPrice", land.ClaimPrice)); + parameters.Add(_Database.CreateParameter("GroupUUID", land.GroupID)); + parameters.Add(_Database.CreateParameter("SalePrice", land.SalePrice)); + parameters.Add(_Database.CreateParameter("LandStatus", (int)land.Status)); //Enum. libsecondlife.Parcel.ParcelStatus + parameters.Add(_Database.CreateParameter("LandFlags", land.Flags)); + parameters.Add(_Database.CreateParameter("LandingType", Convert.ToInt32( land.LandingType) )); + parameters.Add(_Database.CreateParameter("MediaAutoScale", Convert.ToInt32( land.MediaAutoScale ))); + parameters.Add(_Database.CreateParameter("MediaTextureUUID", land.MediaID)); + parameters.Add(_Database.CreateParameter("MediaURL", land.MediaURL)); + parameters.Add(_Database.CreateParameter("MusicURL", land.MusicURL)); + parameters.Add(_Database.CreateParameter("PassHours", land.PassHours)); + parameters.Add(_Database.CreateParameter("PassPrice", land.PassPrice)); + parameters.Add(_Database.CreateParameter("SnapshotUUID", land.SnapshotID)); + parameters.Add(_Database.CreateParameter("UserLocationX", land.UserLocation.X)); + parameters.Add(_Database.CreateParameter("UserLocationY", land.UserLocation.Y)); + parameters.Add(_Database.CreateParameter("UserLocationZ", land.UserLocation.Z)); + parameters.Add(_Database.CreateParameter("UserLookAtX", land.UserLookAt.X)); + parameters.Add(_Database.CreateParameter("UserLookAtY", land.UserLookAt.Y)); + parameters.Add(_Database.CreateParameter("UserLookAtZ", land.UserLookAt.Z)); + parameters.Add(_Database.CreateParameter("AuthBuyerID", land.AuthBuyerID)); + parameters.Add(_Database.CreateParameter("OtherCleanTime", land.OtherCleanTime)); + + return parameters.ToArray(); + } + + /// + /// Creates the land access parameters. + /// + /// parcel access entry. + /// parcel ID. + /// + private NpgsqlParameter[] CreateLandAccessParameters(LandAccessEntry parcelAccessEntry, UUID parcelID) + { + List parameters = new List(); + + parameters.Add(_Database.CreateParameter("LandUUID", parcelID)); + parameters.Add(_Database.CreateParameter("AccessUUID", parcelAccessEntry.AgentID)); + parameters.Add(_Database.CreateParameter("Flags", parcelAccessEntry.Flags)); + parameters.Add(_Database.CreateParameter("Expires", parcelAccessEntry.Expires)); + + return parameters.ToArray(); + } + + /// + /// Creates the prim parameters for storing in DB. + /// + /// Basic data of SceneObjectpart prim. + /// The scenegroup ID. + /// The region ID. + /// + private NpgsqlParameter[] CreatePrimParameters(SceneObjectPart prim, UUID sceneGroupID, UUID regionUUID) + { + List parameters = new List(); + + parameters.Add(_Database.CreateParameter("UUID", prim.UUID)); + parameters.Add(_Database.CreateParameter("RegionUUID", regionUUID)); + parameters.Add(_Database.CreateParameter("CreationDate", prim.CreationDate)); + parameters.Add(_Database.CreateParameter("Name", prim.Name)); + parameters.Add(_Database.CreateParameter("SceneGroupID", sceneGroupID)); + // the UUID of the root part for this SceneObjectGroup + // various text fields + parameters.Add(_Database.CreateParameter("Text", prim.Text)); + parameters.Add(_Database.CreateParameter("ColorR", prim.Color.R)); + parameters.Add(_Database.CreateParameter("ColorG", prim.Color.G)); + parameters.Add(_Database.CreateParameter("ColorB", prim.Color.B)); + parameters.Add(_Database.CreateParameter("ColorA", prim.Color.A)); + parameters.Add(_Database.CreateParameter("Description", prim.Description)); + parameters.Add(_Database.CreateParameter("SitName", prim.SitName)); + parameters.Add(_Database.CreateParameter("TouchName", prim.TouchName)); + // permissions + parameters.Add(_Database.CreateParameter("ObjectFlags", (uint)prim.Flags)); + parameters.Add(_Database.CreateParameter("CreatorID", prim.CreatorID)); + parameters.Add(_Database.CreateParameter("OwnerID", prim.OwnerID)); + parameters.Add(_Database.CreateParameter("GroupID", prim.GroupID)); + parameters.Add(_Database.CreateParameter("LastOwnerID", prim.LastOwnerID)); + parameters.Add(_Database.CreateParameter("OwnerMask", prim.OwnerMask)); + parameters.Add(_Database.CreateParameter("NextOwnerMask", prim.NextOwnerMask)); + parameters.Add(_Database.CreateParameter("GroupMask", prim.GroupMask)); + parameters.Add(_Database.CreateParameter("EveryoneMask", prim.EveryoneMask)); + parameters.Add(_Database.CreateParameter("BaseMask", prim.BaseMask)); + // vectors + parameters.Add(_Database.CreateParameter("PositionX", prim.OffsetPosition.X)); + parameters.Add(_Database.CreateParameter("PositionY", prim.OffsetPosition.Y)); + parameters.Add(_Database.CreateParameter("PositionZ", prim.OffsetPosition.Z)); + parameters.Add(_Database.CreateParameter("GroupPositionX", prim.GroupPosition.X)); + parameters.Add(_Database.CreateParameter("GroupPositionY", prim.GroupPosition.Y)); + parameters.Add(_Database.CreateParameter("GroupPositionZ", prim.GroupPosition.Z)); + parameters.Add(_Database.CreateParameter("VelocityX", prim.Velocity.X)); + parameters.Add(_Database.CreateParameter("VelocityY", prim.Velocity.Y)); + parameters.Add(_Database.CreateParameter("VelocityZ", prim.Velocity.Z)); + parameters.Add(_Database.CreateParameter("AngularVelocityX", prim.AngularVelocity.X)); + parameters.Add(_Database.CreateParameter("AngularVelocityY", prim.AngularVelocity.Y)); + parameters.Add(_Database.CreateParameter("AngularVelocityZ", prim.AngularVelocity.Z)); + parameters.Add(_Database.CreateParameter("AccelerationX", prim.Acceleration.X)); + parameters.Add(_Database.CreateParameter("AccelerationY", prim.Acceleration.Y)); + parameters.Add(_Database.CreateParameter("AccelerationZ", prim.Acceleration.Z)); + // quaternions + parameters.Add(_Database.CreateParameter("RotationX", prim.RotationOffset.X)); + parameters.Add(_Database.CreateParameter("RotationY", prim.RotationOffset.Y)); + parameters.Add(_Database.CreateParameter("RotationZ", prim.RotationOffset.Z)); + parameters.Add(_Database.CreateParameter("RotationW", prim.RotationOffset.W)); + + // Sit target + Vector3 sitTargetPos = prim.SitTargetPositionLL; + parameters.Add(_Database.CreateParameter("SitTargetOffsetX", sitTargetPos.X)); + parameters.Add(_Database.CreateParameter("SitTargetOffsetY", sitTargetPos.Y)); + parameters.Add(_Database.CreateParameter("SitTargetOffsetZ", sitTargetPos.Z)); + + Quaternion sitTargetOrient = prim.SitTargetOrientationLL; + parameters.Add(_Database.CreateParameter("SitTargetOrientW", sitTargetOrient.W)); + parameters.Add(_Database.CreateParameter("SitTargetOrientX", sitTargetOrient.X)); + parameters.Add(_Database.CreateParameter("SitTargetOrientY", sitTargetOrient.Y)); + parameters.Add(_Database.CreateParameter("SitTargetOrientZ", sitTargetOrient.Z)); + + parameters.Add(_Database.CreateParameter("PayPrice", prim.PayPrice[0])); + parameters.Add(_Database.CreateParameter("PayButton1", prim.PayPrice[1])); + parameters.Add(_Database.CreateParameter("PayButton2", prim.PayPrice[2])); + parameters.Add(_Database.CreateParameter("PayButton3", prim.PayPrice[3])); + parameters.Add(_Database.CreateParameter("PayButton4", prim.PayPrice[4])); + + if ((prim.SoundFlags & 1) != 0) // Looped + { + parameters.Add(_Database.CreateParameter("LoopedSound", prim.Sound)); + parameters.Add(_Database.CreateParameter("LoopedSoundGain", prim.SoundGain)); + } + else + { + parameters.Add(_Database.CreateParameter("LoopedSound", UUID.Zero)); + parameters.Add(_Database.CreateParameter("LoopedSoundGain", 0.0f)); + } + + parameters.Add(_Database.CreateParameter("TextureAnimation", prim.TextureAnimation)); + parameters.Add(_Database.CreateParameter("ParticleSystem", prim.ParticleSystem)); + + parameters.Add(_Database.CreateParameter("OmegaX", prim.AngularVelocity.X)); + parameters.Add(_Database.CreateParameter("OmegaY", prim.AngularVelocity.Y)); + parameters.Add(_Database.CreateParameter("OmegaZ", prim.AngularVelocity.Z)); + + parameters.Add(_Database.CreateParameter("CameraEyeOffsetX", prim.GetCameraEyeOffset().X)); + parameters.Add(_Database.CreateParameter("CameraEyeOffsetY", prim.GetCameraEyeOffset().Y)); + parameters.Add(_Database.CreateParameter("CameraEyeOffsetZ", prim.GetCameraEyeOffset().Z)); + + parameters.Add(_Database.CreateParameter("CameraAtOffsetX", prim.GetCameraAtOffset().X)); + parameters.Add(_Database.CreateParameter("CameraAtOffsetY", prim.GetCameraAtOffset().Y)); + parameters.Add(_Database.CreateParameter("CameraAtOffsetZ", prim.GetCameraAtOffset().Z)); + + if (prim.GetForceMouselook()) + parameters.Add(_Database.CreateParameter("ForceMouselook", 1)); + else + parameters.Add(_Database.CreateParameter("ForceMouselook", 0)); + + parameters.Add(_Database.CreateParameter("ScriptAccessPin", prim.ScriptAccessPin)); + + if (prim.AllowedDrop) + parameters.Add(_Database.CreateParameter("AllowedDrop", 1)); + else + parameters.Add(_Database.CreateParameter("AllowedDrop", 0)); + + if (prim.DIE_AT_EDGE) + parameters.Add(_Database.CreateParameter("DieAtEdge", 1)); + else + parameters.Add(_Database.CreateParameter("DieAtEdge", 0)); + + parameters.Add(_Database.CreateParameter("SalePrice", prim.SalePrice)); + parameters.Add(_Database.CreateParameter("SaleType", prim.ObjectSaleType)); + + byte clickAction = prim.ClickAction; + parameters.Add(_Database.CreateParameter("ClickAction", clickAction)); + + parameters.Add(_Database.CreateParameter("Material", prim.Material)); + + parameters.Add(_Database.CreateParameter("CollisionSound", prim.CollisionSound)); + parameters.Add(_Database.CreateParameter("CollisionSoundVolume", prim.CollisionSoundVolume)); + + parameters.Add(_Database.CreateParameter("PassTouches", prim.PassTouches)); + + parameters.Add(_Database.CreateParameter("LinkNumber", prim.LinkNum)); + parameters.Add(_Database.CreateParameter("MediaURL", prim.MediaUrl)); + + if (prim.DynAttrs.CountNamespaces > 0) + parameters.Add(_Database.CreateParameter("DynAttrs", prim.DynAttrs.ToXml())); + else + parameters.Add(_Database.CreateParameter("DynAttrs", null)); + + parameters.Add(_Database.CreateParameter("PhysicsShapeType", prim.PhysicsShapeType)); + parameters.Add(_Database.CreateParameter("Density", (double)prim.Density)); + parameters.Add(_Database.CreateParameter("GravityModifier", (double)prim.GravityModifier)); + parameters.Add(_Database.CreateParameter("Friction", (double)prim.Friction)); + parameters.Add(_Database.CreateParameter("Restitution", (double)prim.Restitution)); + + return parameters.ToArray(); + } + + /// + /// Creates the primshape parameters for stroing in DB. + /// + /// Basic data of SceneObjectpart prim. + /// The scene group ID. + /// The region UUID. + /// + private NpgsqlParameter[] CreatePrimShapeParameters(SceneObjectPart prim, UUID sceneGroupID, UUID regionUUID) + { + List parameters = new List(); + + PrimitiveBaseShape s = prim.Shape; + parameters.Add(_Database.CreateParameter("UUID", prim.UUID)); + // shape is an enum + parameters.Add(_Database.CreateParameter("Shape", 0)); + // vectors + parameters.Add(_Database.CreateParameter("ScaleX", s.Scale.X)); + parameters.Add(_Database.CreateParameter("ScaleY", s.Scale.Y)); + parameters.Add(_Database.CreateParameter("ScaleZ", s.Scale.Z)); + // paths + parameters.Add(_Database.CreateParameter("PCode", s.PCode)); + parameters.Add(_Database.CreateParameter("PathBegin", s.PathBegin)); + parameters.Add(_Database.CreateParameter("PathEnd", s.PathEnd)); + parameters.Add(_Database.CreateParameter("PathScaleX", s.PathScaleX)); + parameters.Add(_Database.CreateParameter("PathScaleY", s.PathScaleY)); + parameters.Add(_Database.CreateParameter("PathShearX", s.PathShearX)); + parameters.Add(_Database.CreateParameter("PathShearY", s.PathShearY)); + parameters.Add(_Database.CreateParameter("PathSkew", s.PathSkew)); + parameters.Add(_Database.CreateParameter("PathCurve", s.PathCurve)); + parameters.Add(_Database.CreateParameter("PathRadiusOffset", s.PathRadiusOffset)); + parameters.Add(_Database.CreateParameter("PathRevolutions", s.PathRevolutions)); + parameters.Add(_Database.CreateParameter("PathTaperX", s.PathTaperX)); + parameters.Add(_Database.CreateParameter("PathTaperY", s.PathTaperY)); + parameters.Add(_Database.CreateParameter("PathTwist", s.PathTwist)); + parameters.Add(_Database.CreateParameter("PathTwistBegin", s.PathTwistBegin)); + // profile + parameters.Add(_Database.CreateParameter("ProfileBegin", s.ProfileBegin)); + parameters.Add(_Database.CreateParameter("ProfileEnd", s.ProfileEnd)); + parameters.Add(_Database.CreateParameter("ProfileCurve", s.ProfileCurve)); + parameters.Add(_Database.CreateParameter("ProfileHollow", s.ProfileHollow)); + parameters.Add(_Database.CreateParameter("Texture", s.TextureEntry)); + parameters.Add(_Database.CreateParameter("ExtraParams", s.ExtraParams)); + parameters.Add(_Database.CreateParameter("State", s.State)); + + if (null == s.Media) + { + parameters.Add(_Database.CreateParameter("Media", DBNull.Value)); + } + else + { + parameters.Add(_Database.CreateParameter("Media", s.Media.ToXml())); + } + + return parameters.ToArray(); + } + + #endregion + + #endregion + + private void LoadSpawnPoints(RegionSettings rs) + { + rs.ClearSpawnPoints(); + + string sql = @"SELECT ""Yaw"", ""Pitch"", ""Distance"" FROM spawn_points WHERE ""RegionUUID"" = :RegionUUID"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("RegionUUID", rs.RegionUUID)); + conn.Open(); + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + if (reader.Read()) + { + SpawnPoint sp = new SpawnPoint(); + + sp.Yaw = (float)reader["Yaw"]; + sp.Pitch = (float)reader["Pitch"]; + sp.Distance = (float)reader["Distance"]; + + rs.AddSpawnPoint(sp); + } + } + } + } + + private void SaveSpawnPoints(RegionSettings rs) + { + string sql = @"DELETE FROM spawn_points WHERE ""RegionUUID"" = :RegionUUID"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("RegionUUID", rs.RegionUUID)); + conn.Open(); + cmd.ExecuteNonQuery(); + } + foreach (SpawnPoint p in rs.SpawnPoints()) + { + sql = @"INSERT INTO spawn_points (""RegionUUID"", ""Yaw"", ""Pitch"", ""Distance"") VALUES (:RegionUUID, :Yaw, :Pitch, :Distance)"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("RegionUUID", rs.RegionUUID)); + cmd.Parameters.Add(_Database.CreateParameter("Yaw", p.Yaw)); + cmd.Parameters.Add(_Database.CreateParameter("Pitch", p.Pitch)); + cmd.Parameters.Add(_Database.CreateParameter("Distance", p.Distance)); + conn.Open(); + cmd.ExecuteNonQuery(); + } + } + } + + public UUID[] GetObjectIDs(UUID regionID) + { + return new UUID[0]; + } + + public void SaveExtra(UUID regionID, string name, string value) + { + } + + public void RemoveExtra(UUID regionID, string name) + { + } + + public Dictionary GetExtra(UUID regionID) + { + return null; + } + } +} diff --git a/OpenSim/Data/PGSQL/PGSQLUserAccountData.cs b/OpenSim/Data/PGSQL/PGSQLUserAccountData.cs new file mode 100644 index 0000000000..a5868fea89 --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLUserAccountData.cs @@ -0,0 +1,330 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Data; +using OpenMetaverse; +using OpenSim.Framework; +using System.Text; +using Npgsql; +using log4net; +using System.Reflection; + +namespace OpenSim.Data.PGSQL +{ + public class PGSQLUserAccountData : PGSQLGenericTableHandler,IUserAccountData + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + + public PGSQLUserAccountData(string connectionString, string realm) : + base(connectionString, realm, "UserAccount") + { + } + + /* + private string m_Realm; + private List m_ColumnNames = null; + private PGSQLManager m_database; + + public PGSQLUserAccountData(string connectionString, string realm) : + base(connectionString, realm, "UserAccount") + { + m_Realm = realm; + m_ConnectionString = connectionString; + m_database = new PGSQLManager(connectionString); + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + { + conn.Open(); + Migration m = new Migration(conn, GetType().Assembly, "UserAccount"); + m.Update(); + } + } + */ + /* + public List Query(UUID principalID, UUID scopeID, string query) + { + return null; + } + */ + /* + public override UserAccountData[] Get(string[] fields, string[] keys) + { + UserAccountData[] retUA = base.Get(fields,keys); + + if (retUA.Length > 0) + { + Dictionary data = retUA[0].Data; + Dictionary data2 = new Dictionary(); + + foreach (KeyValuePair chave in data) + { + string s2 = chave.Key; + + data2[s2] = chave.Value; + + if (!m_FieldTypes.ContainsKey(chave.Key)) + { + string tipo = ""; + m_FieldTypes.TryGetValue(chave.Key, out tipo); + m_FieldTypes.Add(s2, tipo); + } + } + foreach (KeyValuePair chave in data2) + { + if (!retUA[0].Data.ContainsKey(chave.Key)) + retUA[0].Data.Add(chave.Key, chave.Value); + } + } + + return retUA; + } + */ + /* + public UserAccountData Get(UUID principalID, UUID scopeID) + { + UserAccountData ret = new UserAccountData(); + ret.Data = new Dictionary(); + + string sql = string.Format(@"select * from {0} where ""PrincipalID"" = :principalID", m_Realm); + if (scopeID != UUID.Zero) + sql += @" and ""ScopeID"" = :scopeID"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(m_database.CreateParameter("principalID", principalID)); + cmd.Parameters.Add(m_database.CreateParameter("scopeID", scopeID)); + + conn.Open(); + using (NpgsqlDataReader result = cmd.ExecuteReader()) + { + if (result.Read()) + { + ret.PrincipalID = principalID; + UUID scope; + UUID.TryParse(result["scopeid"].ToString(), out scope); + ret.ScopeID = scope; + + if (m_ColumnNames == null) + { + m_ColumnNames = new List(); + + DataTable schemaTable = result.GetSchemaTable(); + foreach (DataRow row in schemaTable.Rows) + m_ColumnNames.Add(row["ColumnName"].ToString()); + } + + foreach (string s in m_ColumnNames) + { + string s2 = s; + if (s2 == "uuid") + continue; + if (s2 == "scopeid") + continue; + + ret.Data[s] = result[s].ToString(); + } + return ret; + } + } + } + return null; + } + + + public override bool Store(UserAccountData data) + { + if (data.Data.ContainsKey("PrincipalID")) + data.Data.Remove("PrincipalID"); + if (data.Data.ContainsKey("ScopeID")) + data.Data.Remove("ScopeID"); + + string[] fields = new List(data.Data.Keys).ToArray(); + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + m_log.DebugFormat("[USER]: Try to update user {0} {1}", data.FirstName, data.LastName); + + StringBuilder updateBuilder = new StringBuilder(); + updateBuilder.AppendFormat("update {0} set ", m_Realm); + bool first = true; + foreach (string field in fields) + { + if (!first) + updateBuilder.Append(", "); + updateBuilder.AppendFormat("\"{0}\" = :{0}", field); + + first = false; + if (m_FieldTypes.ContainsKey(field)) + cmd.Parameters.Add(m_database.CreateParameter("" + field, data.Data[field], m_FieldTypes[field])); + else + cmd.Parameters.Add(m_database.CreateParameter("" + field, data.Data[field])); + } + + updateBuilder.Append(" where \"PrincipalID\" = :principalID"); + + if (data.ScopeID != UUID.Zero) + updateBuilder.Append(" and \"ScopeID\" = :scopeID"); + + cmd.CommandText = updateBuilder.ToString(); + cmd.Connection = conn; + cmd.Parameters.Add(m_database.CreateParameter("principalID", data.PrincipalID)); + cmd.Parameters.Add(m_database.CreateParameter("scopeID", data.ScopeID)); + + m_log.DebugFormat("[USER]: SQL update user {0} ", cmd.CommandText); + + conn.Open(); + + m_log.DebugFormat("[USER]: CON opened update user {0} ", cmd.CommandText); + + int conta = 0; + try + { + conta = cmd.ExecuteNonQuery(); + } + catch (Exception e){ + m_log.ErrorFormat("[USER]: ERROR opened update user {0} ", e.Message); + } + + + if (conta < 1) + { + m_log.DebugFormat("[USER]: Try to insert user {0} {1}", data.FirstName, data.LastName); + + StringBuilder insertBuilder = new StringBuilder(); + insertBuilder.AppendFormat(@"insert into {0} (""PrincipalID"", ""ScopeID"", ""FirstName"", ""LastName"", """, m_Realm); + insertBuilder.Append(String.Join(@""", """, fields)); + insertBuilder.Append(@""") values (:principalID, :scopeID, :FirstName, :LastName, :"); + insertBuilder.Append(String.Join(", :", fields)); + insertBuilder.Append(");"); + + cmd.Parameters.Add(m_database.CreateParameter("FirstName", data.FirstName)); + cmd.Parameters.Add(m_database.CreateParameter("LastName", data.LastName)); + + cmd.CommandText = insertBuilder.ToString(); + + if (cmd.ExecuteNonQuery() < 1) + { + return false; + } + } + else + m_log.DebugFormat("[USER]: User {0} {1} exists", data.FirstName, data.LastName); + } + return true; + } + + + public bool Store(UserAccountData data, UUID principalID, string token) + { + return false; + } + + + public bool SetDataItem(UUID principalID, string item, string value) + { + string sql = string.Format(@"update {0} set {1} = :{1} where ""UUID"" = :UUID", m_Realm, item); + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + if (m_FieldTypes.ContainsKey(item)) + cmd.Parameters.Add(m_database.CreateParameter("" + item, value, m_FieldTypes[item])); + else + cmd.Parameters.Add(m_database.CreateParameter("" + item, value)); + + cmd.Parameters.Add(m_database.CreateParameter("UUID", principalID)); + conn.Open(); + + if (cmd.ExecuteNonQuery() > 0) + return true; + } + return false; + } + */ + /* + public UserAccountData[] Get(string[] keys, string[] vals) + { + return null; + } + */ + + public UserAccountData[] GetUsers(UUID scopeID, string query) + { + string[] words = query.Split(new char[] { ' ' }); + + for (int i = 0; i < words.Length; i++) + { + if (words[i].Length < 3) + { + if (i != words.Length - 1) + Array.Copy(words, i + 1, words, i, words.Length - i - 1); + Array.Resize(ref words, words.Length - 1); + } + } + + if (words.Length == 0) + return new UserAccountData[0]; + + if (words.Length > 2) + return new UserAccountData[0]; + + string sql = ""; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + if (words.Length == 1) + { + sql = String.Format(@"select * from {0} where (""ScopeID""=:ScopeID or ""ScopeID""='00000000-0000-0000-0000-000000000000') and (""FirstName"" ilike :search or ""LastName"" ilike :search)", m_Realm); + cmd.Parameters.Add(m_database.CreateParameter("scopeID", scopeID)); + cmd.Parameters.Add(m_database.CreateParameter("search", "%" + words[0] + "%")); + } + else + { + sql = String.Format(@"select * from {0} where (""ScopeID""=:ScopeID or ""ScopeID""='00000000-0000-0000-0000-000000000000') and (""FirstName"" ilike :searchFirst or ""LastName"" ilike :searchLast)", m_Realm); + cmd.Parameters.Add(m_database.CreateParameter("searchFirst", "%" + words[0] + "%")); + cmd.Parameters.Add(m_database.CreateParameter("searchLast", "%" + words[1] + "%")); + cmd.Parameters.Add(m_database.CreateParameter("ScopeID", scopeID.ToString())); + } + cmd.Connection = conn; + cmd.CommandText = sql; + conn.Open(); + return DoQuery(cmd); + } + } + + public UserAccountData[] GetUsersWhere(UUID scopeID, string where) + { + return null; + } + } +} diff --git a/OpenSim/Data/PGSQL/PGSQLUserProfilesData.cs b/OpenSim/Data/PGSQL/PGSQLUserProfilesData.cs new file mode 100644 index 0000000000..f4e41b47dd --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLUserProfilesData.cs @@ -0,0 +1,1073 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Data; +using System.Reflection; +using OpenSim.Data; +using OpenSim.Framework; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using log4net; +using Npgsql; + +namespace OpenSim.Data.PGSQL +{ + public class UserProfilesData: IProfilesData + { + static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + #region Properites + string ConnectionString + { + get; set; + } + + protected object Lock + { + get; set; + } + + protected virtual Assembly Assembly + { + get { return GetType().Assembly; } + } + + #endregion Properties + + #region class Member Functions + public UserProfilesData(string connectionString) + { + ConnectionString = connectionString; + Init(); + } + + void Init() + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(ConnectionString)) + { + dbcon.Open(); + + Migration m = new Migration(dbcon, Assembly, "UserProfiles"); + m.Update(); + } + } + #endregion Member Functions + + #region Classifieds Queries + /// + /// Gets the classified records. + /// + /// + /// Array of classified records + /// + /// + /// Creator identifier. + /// + public OSDArray GetClassifiedRecords(UUID creatorId) + { + OSDArray data = new OSDArray(); + + using (NpgsqlConnection dbcon = new NpgsqlConnection(ConnectionString)) + { + string query = @"SELECT ""classifieduuid"", ""name"" FROM classifieds WHERE ""creatoruuid"" = :Id"; + dbcon.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("Id", creatorId); + using( NpgsqlDataReader reader = cmd.ExecuteReader(CommandBehavior.Default)) + { + if(reader.HasRows) + { + while (reader.Read()) + { + OSDMap n = new OSDMap(); + UUID Id = UUID.Zero; + + string Name = null; + try + { + UUID.TryParse(Convert.ToString( reader["classifieduuid"]), out Id); + Name = Convert.ToString(reader["name"]); + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": UserAccount exception {0}", e.Message); + } + n.Add("classifieduuid", OSD.FromUUID(Id)); + n.Add("name", OSD.FromString(Name)); + data.Add(n); + } + } + } + } + } + return data; + } + + public bool UpdateClassifiedRecord(UserClassifiedAdd ad, ref string result) + { + string query = @"INSERT INTO classifieds ( ""classifieduuid"",""creatoruuid"", ""creationdate"", ""expirationdate"", ""category"", + ""name"", ""description"", ""parceluuid"", ""parentestate"", ""snapshotuuid"", ""simname"", + ""posglobal"", ""parcelname"", ""classifiedflags"", ""priceforlisting"") + Select :ClassifiedId, :CreatorId, :CreatedDate, :ExpirationDate, :Category, + :Name, :Description, :ParcelId, :ParentEstate, :SnapshotId, :SimName + :GlobalPos, :ParcelName, :Flags, :ListingPrice + Where not exists( Select ""classifieduuid"" from classifieds where ""classifieduuid"" = :ClassifiedId ); + + update classifieds + set category =:Category, + expirationdate = :ExpirationDate, + name = :Name, + description = :Description, + parentestate = :ParentEstate, + posglobal = :GlobalPos, + parcelname = :ParcelName, + classifiedflags = :Flags, + priceforlisting = :ListingPrice, + snapshotuuid = :SnapshotId + where classifieduuid = :ClassifiedId ; + "; + + if(string.IsNullOrEmpty(ad.ParcelName)) + ad.ParcelName = "Unknown"; + if(ad.ParcelId == null) + ad.ParcelId = UUID.Zero; + if(string.IsNullOrEmpty(ad.Description)) + ad.Description = "No Description"; + + DateTime epoch = new DateTime(1970, 1, 1); + DateTime now = DateTime.Now; + TimeSpan epochnow = now - epoch; + TimeSpan duration; + DateTime expiration; + TimeSpan epochexp; + + if(ad.Flags == 2) + { + duration = new TimeSpan(7,0,0,0); + expiration = now.Add(duration); + epochexp = expiration - epoch; + } + else + { + duration = new TimeSpan(365,0,0,0); + expiration = now.Add(duration); + epochexp = expiration - epoch; + } + ad.CreationDate = (int)epochnow.TotalSeconds; + ad.ExpirationDate = (int)epochexp.TotalSeconds; + + try + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(ConnectionString)) + { + dbcon.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("ClassifiedId", ad.ClassifiedId.ToString()); + cmd.Parameters.AddWithValue("CreatorId", ad.CreatorId.ToString()); + cmd.Parameters.AddWithValue("CreatedDate", ad.CreationDate.ToString()); + cmd.Parameters.AddWithValue("ExpirationDate", ad.ExpirationDate.ToString()); + cmd.Parameters.AddWithValue("Category", ad.Category.ToString()); + cmd.Parameters.AddWithValue("Name", ad.Name.ToString()); + cmd.Parameters.AddWithValue("Description", ad.Description.ToString()); + cmd.Parameters.AddWithValue("ParcelId", ad.ParcelId.ToString()); + cmd.Parameters.AddWithValue("ParentEstate", ad.ParentEstate.ToString()); + cmd.Parameters.AddWithValue("SnapshotId", ad.SnapshotId.ToString ()); + cmd.Parameters.AddWithValue("SimName", ad.SimName.ToString()); + cmd.Parameters.AddWithValue("GlobalPos", ad.GlobalPos.ToString()); + cmd.Parameters.AddWithValue("ParcelName", ad.ParcelName.ToString()); + cmd.Parameters.AddWithValue("Flags", ad.Flags.ToString()); + cmd.Parameters.AddWithValue("ListingPrice", ad.Price.ToString ()); + + cmd.ExecuteNonQuery(); + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": ClassifiedesUpdate exception {0}", e.Message); + result = e.Message; + return false; + } + return true; + } + + + public bool DeleteClassifiedRecord(UUID recordId) + { + string query = string.Empty; + + query = @"DELETE FROM classifieds WHERE classifieduuid = :ClasifiedId ;"; + + try + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(ConnectionString)) + { + dbcon.Open(); + + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("ClassifiedId", recordId.ToString()); + + lock(Lock) + { + cmd.ExecuteNonQuery(); + } + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": DeleteClassifiedRecord exception {0}", e.Message); + return false; + } + return true; + } + + public bool GetClassifiedInfo(ref UserClassifiedAdd ad, ref string result) + { + string query = string.Empty; + + query += "SELECT * FROM classifieds WHERE "; + query += "classifieduuid = :AdId"; + + try + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(ConnectionString)) + { + dbcon.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("AdId", ad.ClassifiedId.ToString()); + + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + if(reader.Read ()) + { + ad.CreatorId = GetUUID(reader["creatoruuid"]); + ad.ParcelId = GetUUID(reader["parceluuid"]); + ad.SnapshotId = GetUUID(reader["snapshotuuid"]); + ad.CreationDate = Convert.ToInt32(reader["creationdate"]); + ad.ExpirationDate = Convert.ToInt32(reader["expirationdate"]); + ad.ParentEstate = Convert.ToInt32(reader["parentestate"]); + ad.Flags = (byte)Convert.ToInt16(reader["classifiedflags"]); + ad.Category = Convert.ToInt32(reader["category"]); + ad.Price = Convert.ToInt16(reader["priceforlisting"]); + ad.Name = reader["name"].ToString(); + ad.Description = reader["description"].ToString(); + ad.SimName = reader["simname"].ToString(); + ad.GlobalPos = reader["posglobal"].ToString(); + ad.ParcelName = reader["parcelname"].ToString(); + + } + } + } + dbcon.Close(); + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": GetPickInfo exception {0}", e.Message); + } + return true; + } + + public static UUID GetUUID( object uuidValue ) { + + UUID ret = UUID.Zero; + + UUID.TryParse(uuidValue.ToString(), out ret); + + return ret; + } + + + #endregion Classifieds Queries + + #region Picks Queries + public OSDArray GetAvatarPicks(UUID avatarId) + { + string query = string.Empty; + + query += "SELECT \"pickuuid\",\"name\" FROM userpicks WHERE "; + query += "creatoruuid = :Id"; + OSDArray data = new OSDArray(); + + try + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(ConnectionString)) + { + dbcon.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("Id", avatarId.ToString()); + + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + if(reader.HasRows) + { + while (reader.Read()) + { + OSDMap record = new OSDMap(); + + record.Add("pickuuid",OSD.FromString((string)reader["pickuuid"])); + record.Add("name",OSD.FromString((string)reader["name"])); + data.Add(record); + } + } + } + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": GetAvatarPicks exception {0}", e.Message); + } + return data; + } + + public UserProfilePick GetPickInfo(UUID avatarId, UUID pickId) + { + string query = string.Empty; + UserProfilePick pick = new UserProfilePick(); + + query += "SELECT * FROM userpicks WHERE "; + query += "creatoruuid = :CreatorId AND "; + query += "pickuuid = :PickId"; + + try + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(ConnectionString)) + { + dbcon.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("CreatorId", avatarId.ToString()); + cmd.Parameters.AddWithValue("PickId", pickId.ToString()); + + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + if(reader.HasRows) + { + reader.Read(); + + string description = (string)reader["description"]; + + if (string.IsNullOrEmpty(description)) + description = "No description given."; + + UUID.TryParse((string)reader["pickuuid"], out pick.PickId); + UUID.TryParse((string)reader["creatoruuid"], out pick.CreatorId); + UUID.TryParse((string)reader["parceluuid"], out pick.ParcelId); + UUID.TryParse((string)reader["snapshotuuid"], out pick.SnapshotId); + pick.GlobalPos = (string)reader["posglobal"]; + bool.TryParse((string)reader["toppick"], out pick.TopPick); + bool.TryParse((string)reader["enabled"], out pick.Enabled); + pick.Name = (string)reader["name"]; + pick.Desc = description; + pick.User = (string)reader["user"]; + pick.OriginalName = (string)reader["originalname"]; + pick.SimName = (string)reader["simname"]; + pick.SortOrder = (int)reader["sortorder"]; + } + } + } + dbcon.Close(); + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": GetPickInfo exception {0}", e.Message); + } + return pick; + } + + public bool UpdatePicksRecord(UserProfilePick pick) + { + string query = string.Empty; + + query = @"INSERT INTO userpicks VALUES ( :PickId, :CreatorId, :TopPick, :ParcelId,:Name, :Desc, :SnapshotId,:User, + :Original, :SimName, :GlobalPos, :SortOrder, :Enabled) + where not exists ( select pickid from userpicks where pickid = :pickid); + + Update userpicks + set parceluuid = :ParcelId, + name = :Name, + description = :Desc, + snapshotuuid = :SnapshotId, + pickuuid = :PickId, + posglobal = :GlobalPos + where pickid = :PickId; + "; + + try + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(ConnectionString)) + { + dbcon.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("PickId", pick.PickId.ToString()); + cmd.Parameters.AddWithValue("CreatorId", pick.CreatorId.ToString()); + cmd.Parameters.AddWithValue("TopPick", pick.TopPick.ToString()); + cmd.Parameters.AddWithValue("ParcelId", pick.ParcelId.ToString()); + cmd.Parameters.AddWithValue("Name", pick.Name.ToString()); + cmd.Parameters.AddWithValue("Desc", pick.Desc.ToString()); + cmd.Parameters.AddWithValue("SnapshotId", pick.SnapshotId.ToString()); + cmd.Parameters.AddWithValue("User", pick.User.ToString()); + cmd.Parameters.AddWithValue("Original", pick.OriginalName.ToString()); + cmd.Parameters.AddWithValue("SimName",pick.SimName.ToString()); + cmd.Parameters.AddWithValue("GlobalPos", pick.GlobalPos); + cmd.Parameters.AddWithValue("SortOrder", pick.SortOrder.ToString ()); + cmd.Parameters.AddWithValue("Enabled", pick.Enabled.ToString()); + + cmd.ExecuteNonQuery(); + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": UpdateAvatarNotes exception {0}", e.Message); + return false; + } + return true; + } + + public bool DeletePicksRecord(UUID pickId) + { + string query = string.Empty; + + query += "DELETE FROM userpicks WHERE "; + query += "pickuuid = :PickId"; + + try + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(ConnectionString)) + { + dbcon.Open(); + + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("PickId", pickId.ToString()); + + cmd.ExecuteNonQuery(); + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": DeleteUserPickRecord exception {0}", e.Message); + return false; + } + return true; + } + #endregion Picks Queries + + #region Avatar Notes Queries + public bool GetAvatarNotes(ref UserProfileNotes notes) + { // WIP + string query = string.Empty; + + query += "SELECT \"notes\" FROM usernotes WHERE "; + query += "useruuid = :Id AND "; + query += "targetuuid = :TargetId"; + OSDArray data = new OSDArray(); + + try + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(ConnectionString)) + { + dbcon.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("Id", notes.UserId.ToString()); + cmd.Parameters.AddWithValue("TargetId", notes.TargetId.ToString()); + + using (NpgsqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if(reader.HasRows) + { + reader.Read(); + notes.Notes = OSD.FromString((string)reader["notes"]); + } + } + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": GetAvatarNotes exception {0}", e.Message); + } + return true; + } + + public bool UpdateAvatarNotes(ref UserProfileNotes note, ref string result) + { + string query = string.Empty; + bool remove; + + if(string.IsNullOrEmpty(note.Notes)) + { + remove = true; + query += "DELETE FROM usernotes WHERE "; + query += "useruuid=:UserId AND "; + query += "targetuuid=:TargetId"; + } + else + { + remove = false; + query = @"INSERT INTO usernotes VALUES ( :UserId, :TargetId, :Notes ) + where not exists ( Select useruuid from usernotes where useruuid = :UserId and targetuuid = :TargetId ); + + update usernotes + set notes = :Notes + where useruuid = :UserId + and targetuuid = :TargetId; + "; + } + + try + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(ConnectionString)) + { + dbcon.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + if(!remove) + cmd.Parameters.AddWithValue("Notes", note.Notes); + cmd.Parameters.AddWithValue("TargetId", note.TargetId.ToString ()); + cmd.Parameters.AddWithValue("UserId", note.UserId.ToString()); + + cmd.ExecuteNonQuery(); + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": UpdateAvatarNotes exception {0}", e.Message); + return false; + } + return true; + + } + #endregion Avatar Notes Queries + + #region Avatar Properties + public bool GetAvatarProperties(ref UserProfileProperties props, ref string result) + { + string query = string.Empty; + + query += "SELECT * FROM userprofile WHERE "; + query += "useruuid = :Id"; + + try + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(ConnectionString)) + { + dbcon.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("Id", props.UserId.ToString()); + + using (NpgsqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if(reader.HasRows) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": Getting data for {0}.", props.UserId); + reader.Read(); + props.WebUrl = (string)reader["profileURL"]; + UUID.TryParse((string)reader["profileImage"], out props.ImageId); + props.AboutText = (string)reader["profileAboutText"]; + UUID.TryParse((string)reader["profileFirstImage"], out props.FirstLifeImageId); + props.FirstLifeText = (string)reader["profileFirstText"]; + UUID.TryParse((string)reader["profilePartner"], out props.PartnerId); + props.WantToMask = (int)reader["profileWantToMask"]; + props.WantToText = (string)reader["profileWantToText"]; + props.SkillsMask = (int)reader["profileSkillsMask"]; + props.SkillsText = (string)reader["profileSkillsText"]; + props.Language = (string)reader["profileLanguages"]; + } + else + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": No data for {0}", props.UserId); + + props.WebUrl = string.Empty; + props.ImageId = UUID.Zero; + props.AboutText = string.Empty; + props.FirstLifeImageId = UUID.Zero; + props.FirstLifeText = string.Empty; + props.PartnerId = UUID.Zero; + props.WantToMask = 0; + props.WantToText = string.Empty; + props.SkillsMask = 0; + props.SkillsText = string.Empty; + props.Language = string.Empty; + props.PublishProfile = false; + props.PublishMature = false; + + query = "INSERT INTO userprofile ("; + query += "useruuid, "; + query += "profilePartner, "; + query += "profileAllowPublish, "; + query += "profileMaturePublish, "; + query += "profileURL, "; + query += "profileWantToMask, "; + query += "profileWantToText, "; + query += "profileSkillsMask, "; + query += "profileSkillsText, "; + query += "profileLanguages, "; + query += "profileImage, "; + query += "profileAboutText, "; + query += "profileFirstImage, "; + query += "profileFirstText) VALUES ("; + query += ":userId, "; + query += ":profilePartner, "; + query += ":profileAllowPublish, "; + query += ":profileMaturePublish, "; + query += ":profileURL, "; + query += ":profileWantToMask, "; + query += ":profileWantToText, "; + query += ":profileSkillsMask, "; + query += ":profileSkillsText, "; + query += ":profileLanguages, "; + query += ":profileImage, "; + query += ":profileAboutText, "; + query += ":profileFirstImage, "; + query += ":profileFirstText)"; + + dbcon.Close(); + dbcon.Open(); + + using (NpgsqlCommand put = new NpgsqlCommand(query, dbcon)) + { + put.Parameters.AddWithValue("userId", props.UserId.ToString()); + put.Parameters.AddWithValue("profilePartner", props.PartnerId.ToString()); + put.Parameters.AddWithValue("profileAllowPublish", props.PublishProfile); + put.Parameters.AddWithValue("profileMaturePublish", props.PublishMature); + put.Parameters.AddWithValue("profileURL", props.WebUrl); + put.Parameters.AddWithValue("profileWantToMask", props.WantToMask); + put.Parameters.AddWithValue("profileWantToText", props.WantToText); + put.Parameters.AddWithValue("profileSkillsMask", props.SkillsMask); + put.Parameters.AddWithValue("profileSkillsText", props.SkillsText); + put.Parameters.AddWithValue("profileLanguages", props.Language); + put.Parameters.AddWithValue("profileImage", props.ImageId.ToString()); + put.Parameters.AddWithValue("profileAboutText", props.AboutText); + put.Parameters.AddWithValue("profileFirstImage", props.FirstLifeImageId.ToString()); + put.Parameters.AddWithValue("profileFirstText", props.FirstLifeText); + + put.ExecuteNonQuery(); + } + } + } + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": Requst properties exception {0}", e.Message); + result = e.Message; + return false; + } + return true; + } + + public bool UpdateAvatarProperties(ref UserProfileProperties props, ref string result) + { + string query = string.Empty; + + query += "UPDATE userprofile SET "; + query += "profileURL=:profileURL, "; + query += "profileImage=:image, "; + query += "profileAboutText=:abouttext,"; + query += "profileFirstImage=:firstlifeimage,"; + query += "profileFirstText=:firstlifetext "; + query += "WHERE useruuid=:uuid"; + + try + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(ConnectionString)) + { + dbcon.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("profileURL", props.WebUrl); + cmd.Parameters.AddWithValue("image", props.ImageId.ToString()); + cmd.Parameters.AddWithValue("abouttext", props.AboutText); + cmd.Parameters.AddWithValue("firstlifeimage", props.FirstLifeImageId.ToString()); + cmd.Parameters.AddWithValue("firstlifetext", props.FirstLifeText); + cmd.Parameters.AddWithValue("uuid", props.UserId.ToString()); + + cmd.ExecuteNonQuery(); + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": AgentPropertiesUpdate exception {0}", e.Message); + + return false; + } + return true; + } + #endregion Avatar Properties + + #region Avatar Interests + public bool UpdateAvatarInterests(UserProfileProperties up, ref string result) + { + string query = string.Empty; + + query += "UPDATE userprofile SET "; + query += "profileWantToMask=:WantMask, "; + query += "profileWantToText=:WantText,"; + query += "profileSkillsMask=:SkillsMask,"; + query += "profileSkillsText=:SkillsText, "; + query += "profileLanguages=:Languages "; + query += "WHERE useruuid=:uuid"; + + try + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(ConnectionString)) + { + dbcon.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("WantMask", up.WantToMask); + cmd.Parameters.AddWithValue("WantText", up.WantToText); + cmd.Parameters.AddWithValue("SkillsMask", up.SkillsMask); + cmd.Parameters.AddWithValue("SkillsText", up.SkillsText); + cmd.Parameters.AddWithValue("Languages", up.Language); + cmd.Parameters.AddWithValue("uuid", up.UserId.ToString()); + + cmd.ExecuteNonQuery(); + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": AgentInterestsUpdate exception {0}", e.Message); + result = e.Message; + return false; + } + return true; + } + #endregion Avatar Interests + + public OSDArray GetUserImageAssets(UUID avatarId) + { + OSDArray data = new OSDArray(); + string query = "SELECT \"snapshotuuid\" FROM {0} WHERE \"creatoruuid\" = :Id"; + + // Get classified image assets + + + try + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(ConnectionString)) + { + dbcon.Open(); + + using (NpgsqlCommand cmd = new NpgsqlCommand(string.Format (query,"\"classifieds\""), dbcon)) + { + cmd.Parameters.AddWithValue("Id", avatarId.ToString()); + + using (NpgsqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if(reader.HasRows) + { + while (reader.Read()) + { + data.Add(new OSDString((string)reader["snapshotuuid"].ToString ())); + } + } + } + } + + dbcon.Close(); + dbcon.Open(); + + using (NpgsqlCommand cmd = new NpgsqlCommand(string.Format (query,"\"userpicks\""), dbcon)) + { + cmd.Parameters.AddWithValue("Id", avatarId.ToString()); + + using (NpgsqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if(reader.HasRows) + { + while (reader.Read()) + { + data.Add(new OSDString((string)reader["snapshotuuid"].ToString ())); + } + } + } + } + + dbcon.Close(); + dbcon.Open(); + + query = "SELECT \"profileImage\", \"profileFirstImage\" FROM \"userprofile\" WHERE \"useruuid\" = :Id"; + + using (NpgsqlCommand cmd = new NpgsqlCommand(string.Format (query,"\"userpicks\""), dbcon)) + { + cmd.Parameters.AddWithValue("Id", avatarId.ToString()); + + using (NpgsqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if(reader.HasRows) + { + while (reader.Read()) + { + data.Add(new OSDString((string)reader["profileImage"].ToString ())); + data.Add(new OSDString((string)reader["profileFirstImage"].ToString ())); + } + } + } + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": GetAvatarNotes exception {0}", e.Message); + } + return data; + } + + #region User Preferences + public OSDArray GetUserPreferences(UUID avatarId) + { + string query = string.Empty; + + query += "SELECT imviaemail,visible,email FROM "; + query += "usersettings WHERE "; + query += "useruuid = :Id"; + + OSDArray data = new OSDArray(); + + try + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(ConnectionString)) + { + dbcon.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("Id", avatarId.ToString()); + + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + if(reader.HasRows) + { + reader.Read(); + OSDMap record = new OSDMap(); + + record.Add("imviaemail",OSD.FromString((string)reader["imviaemail"])); + record.Add("visible",OSD.FromString((string)reader["visible"])); + record.Add("email",OSD.FromString((string)reader["email"])); + data.Add(record); + } + else + { + using (NpgsqlCommand put = new NpgsqlCommand(query, dbcon)) + { + query = "INSERT INTO usersettings VALUES "; + query += "(:Id,'false','false', '')"; + + lock(Lock) + { + put.ExecuteNonQuery(); + } + } + } + } + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": Get preferences exception {0}", e.Message); + } + return data; + } + + public bool UpdateUserPreferences(bool emailIm, bool visible, UUID avatarId ) + { + string query = string.Empty; + + query += "UPDATE userpsettings SET "; + query += "imviaemail=:ImViaEmail, "; + query += "visible=:Visible,"; + query += "WHERE useruuid=:uuid"; + + try + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(ConnectionString)) + { + dbcon.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("ImViaEmail", emailIm.ToString().ToLower ()); + cmd.Parameters.AddWithValue("WantText", visible.ToString().ToLower ()); + cmd.Parameters.AddWithValue("uuid", avatarId.ToString()); + + lock(Lock) + { + cmd.ExecuteNonQuery(); + } + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": AgentInterestsUpdate exception {0}", e.Message); + return false; + } + return true; + } + #endregion User Preferences + + #region Integration + public bool GetUserAppData(ref UserAppData props, ref string result) + { + string query = string.Empty; + + query += "SELECT * FROM userdata WHERE "; + query += "\"UserId\" = :Id AND "; + query += "\"TagId\" = :TagId"; + + try + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(ConnectionString)) + { + dbcon.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("Id", props.UserId.ToString()); + cmd.Parameters.AddWithValue (":TagId", props.TagId.ToString()); + + using (NpgsqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if(reader.HasRows) + { + reader.Read(); + props.DataKey = (string)reader["DataKey"]; + props.DataVal = (string)reader["DataVal"]; + } + else + { + query += "INSERT INTO userdata VALUES ( "; + query += ":UserId,"; + query += ":TagId,"; + query += ":DataKey,"; + query += ":DataVal) "; + + using (NpgsqlCommand put = new NpgsqlCommand(query, dbcon)) + { + put.Parameters.AddWithValue("Id", props.UserId.ToString()); + put.Parameters.AddWithValue("TagId", props.TagId.ToString()); + put.Parameters.AddWithValue("DataKey", props.DataKey.ToString()); + put.Parameters.AddWithValue("DataVal", props.DataVal.ToString()); + + lock(Lock) + { + put.ExecuteNonQuery(); + } + } + } + } + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": Requst application data exception {0}", e.Message); + result = e.Message; + return false; + } + return true; + } + + public bool SetUserAppData(UserAppData props, ref string result) + { + string query = string.Empty; + + query += "UPDATE userdata SET "; + query += "\"TagId\" = :TagId, "; + query += "\"DataKey\" = :DataKey, "; + query += "\"DataVal\" = :DataVal WHERE "; + query += "\"UserId\" = :UserId AND "; + query += "\"TagId\" = :TagId"; + + try + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(ConnectionString)) + { + dbcon.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("UserId", props.UserId.ToString()); + cmd.Parameters.AddWithValue("TagId", props.TagId.ToString ()); + cmd.Parameters.AddWithValue("DataKey", props.DataKey.ToString ()); + cmd.Parameters.AddWithValue("DataVal", props.DataKey.ToString ()); + + lock(Lock) + { + cmd.ExecuteNonQuery(); + } + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": SetUserData exception {0}", e.Message); + return false; + } + return true; + } + #endregion Integration + } +} + diff --git a/OpenSim/Data/PGSQL/PGSQLXAssetData.cs b/OpenSim/Data/PGSQL/PGSQLXAssetData.cs new file mode 100644 index 0000000000..e959619ed0 --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLXAssetData.cs @@ -0,0 +1,535 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Data; +using System.IO; +using System.IO.Compression; +using System.Reflection; +using System.Security.Cryptography; +using System.Text; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Data; +using Npgsql; + +namespace OpenSim.Data.PGSQL +{ + public class PGSQLXAssetData : IXAssetDataPlugin + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected virtual Assembly Assembly + { + get { return GetType().Assembly; } + } + + /// + /// Number of days that must pass before we update the access time on an asset when it has been fetched. + /// + private const int DaysBetweenAccessTimeUpdates = 30; + + private bool m_enableCompression = false; + private string m_connectionString; + private object m_dbLock = new object(); + + /// + /// We can reuse this for all hashing since all methods are single-threaded through m_dbBLock + /// + private HashAlgorithm hasher = new SHA256CryptoServiceProvider(); + + #region IPlugin Members + + public string Version { get { return "1.0.0.0"; } } + + /// + /// Initialises Asset interface + /// + /// + /// Loads and initialises the PGSQL storage plugin. + /// Warns and uses the obsolete pgsql_connection.ini if connect string is empty. + /// Check for migration + /// + /// + /// + /// connect string + public void Initialise(string connect) + { + m_log.ErrorFormat("[PGSQL XASSETDATA]: ***********************************************************"); + m_log.ErrorFormat("[PGSQL XASSETDATA]: ***********************************************************"); + m_log.ErrorFormat("[PGSQL XASSETDATA]: ***********************************************************"); + m_log.ErrorFormat("[PGSQL XASSETDATA]: THIS PLUGIN IS STRICTLY EXPERIMENTAL."); + m_log.ErrorFormat("[PGSQL XASSETDATA]: DO NOT USE FOR ANY DATA THAT YOU DO NOT MIND LOSING."); + m_log.ErrorFormat("[PGSQL XASSETDATA]: DATABASE TABLES CAN CHANGE AT ANY TIME, CAUSING EXISTING DATA TO BE LOST."); + m_log.ErrorFormat("[PGSQL XASSETDATA]: ***********************************************************"); + m_log.ErrorFormat("[PGSQL XASSETDATA]: ***********************************************************"); + m_log.ErrorFormat("[PGSQL XASSETDATA]: ***********************************************************"); + + m_connectionString = connect; + + using (NpgsqlConnection dbcon = new NpgsqlConnection(m_connectionString)) + { + dbcon.Open(); + Migration m = new Migration(dbcon, Assembly, "XAssetStore"); + m.Update(); + } + } + + public void Initialise() + { + throw new NotImplementedException(); + } + + public void Dispose() { } + + /// + /// The name of this DB provider + /// + public string Name + { + get { return "PGSQL XAsset storage engine"; } + } + + #endregion + + #region IAssetDataPlugin Members + + /// + /// Fetch Asset from database + /// + /// Asset UUID to fetch + /// Return the asset + /// On failure : throw an exception and attempt to reconnect to database + public AssetBase GetAsset(UUID assetID) + { +// m_log.DebugFormat("[PGSQL XASSET DATA]: Looking for asset {0}", assetID); + + AssetBase asset = null; + lock (m_dbLock) + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (NpgsqlCommand cmd = new NpgsqlCommand( + @"SELECT ""Name"", ""Description"", ""AccessTime"", ""AssetType"", ""Local"", ""Temporary"", ""AssetFlags"", ""CreatorID"", ""Data"" + FROM XAssetsMeta + JOIN XAssetsData ON XAssetsMeta.Hash = XAssetsData.Hash WHERE ""ID""=:ID", + dbcon)) + { + cmd.Parameters.AddWithValue("ID", assetID.ToString()); + + try + { + using (NpgsqlDataReader dbReader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if (dbReader.Read()) + { + asset = new AssetBase(assetID, (string)dbReader["Name"], (sbyte)dbReader["AssetType"], dbReader["CreatorID"].ToString()); + asset.Data = (byte[])dbReader["Data"]; + asset.Description = (string)dbReader["Description"]; + + string local = dbReader["Local"].ToString(); + if (local.Equals("1") || local.Equals("true", StringComparison.InvariantCultureIgnoreCase)) + asset.Local = true; + else + asset.Local = false; + + asset.Temporary = Convert.ToBoolean(dbReader["Temporary"]); + asset.Flags = (AssetFlags)Convert.ToInt32(dbReader["AssetFlags"]); + + if (m_enableCompression) + { + using (GZipStream decompressionStream = new GZipStream(new MemoryStream(asset.Data), CompressionMode.Decompress)) + { + MemoryStream outputStream = new MemoryStream(); + WebUtil.CopyStream(decompressionStream, outputStream, int.MaxValue); + // int compressedLength = asset.Data.Length; + asset.Data = outputStream.ToArray(); + + // m_log.DebugFormat( + // "[XASSET DB]: Decompressed {0} {1} to {2} bytes from {3}", + // asset.ID, asset.Name, asset.Data.Length, compressedLength); + } + } + + UpdateAccessTime(asset.Metadata, (int)dbReader["AccessTime"]); + } + } + } + catch (Exception e) + { + m_log.Error(string.Format("[PGSQL XASSET DATA]: Failure fetching asset {0}", assetID), e); + } + } + } + } + + return asset; + } + + /// + /// Create an asset in database, or update it if existing. + /// + /// Asset UUID to create + /// On failure : Throw an exception and attempt to reconnect to database + public void StoreAsset(AssetBase asset) + { +// m_log.DebugFormat("[XASSETS DB]: Storing asset {0} {1}", asset.Name, asset.ID); + + lock (m_dbLock) + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (NpgsqlTransaction transaction = dbcon.BeginTransaction()) + { + string assetName = asset.Name; + if (asset.Name.Length > 64) + { + assetName = asset.Name.Substring(0, 64); + m_log.WarnFormat( + "[XASSET DB]: Name '{0}' for asset {1} truncated from {2} to {3} characters on add", + asset.Name, asset.ID, asset.Name.Length, assetName.Length); + } + + string assetDescription = asset.Description; + if (asset.Description.Length > 64) + { + assetDescription = asset.Description.Substring(0, 64); + m_log.WarnFormat( + "[XASSET DB]: Description '{0}' for asset {1} truncated from {2} to {3} characters on add", + asset.Description, asset.ID, asset.Description.Length, assetDescription.Length); + } + + if (m_enableCompression) + { + MemoryStream outputStream = new MemoryStream(); + + using (GZipStream compressionStream = new GZipStream(outputStream, CompressionMode.Compress, false)) + { + // Console.WriteLine(WebUtil.CopyTo(new MemoryStream(asset.Data), compressionStream, int.MaxValue)); + // We have to close the compression stream in order to make sure it writes everything out to the underlying memory output stream. + compressionStream.Close(); + byte[] compressedData = outputStream.ToArray(); + asset.Data = compressedData; + } + } + + byte[] hash = hasher.ComputeHash(asset.Data); + +// m_log.DebugFormat( +// "[XASSET DB]: Compressed data size for {0} {1}, hash {2} is {3}", +// asset.ID, asset.Name, hash, compressedData.Length); + + try + { + using (NpgsqlCommand cmd = + new NpgsqlCommand( + @"insert INTO XAssetsMeta(""ID"", ""Hash"", ""Name"", ""Description"", ""AssetType"", ""Local"", ""Temporary"", ""CreateTime"", ""AccessTime"", ""AssetFlags"", ""CreatorID"") + Select :ID, :Hash, :Name, :Description, :AssetType, :Local, :Temporary, :CreateTime, :AccessTime, :AssetFlags, :CreatorID + where not exists( Select ""ID"" from XAssetsMeta where ""ID"" = :ID ; + + update XAssetsMeta + set ""ID"" = :ID, ""Hash"" = :Hash, ""Name"" = :Name, ""Description"" = :Description, + ""AssetType"" = :AssetType, ""Local"" = :Local, ""Temporary"" = :Temporary, ""CreateTime"" = :CreateTime, + ""AccessTime"" = :AccessTime, ""AssetFlags"" = :AssetFlags, ""CreatorID"" = :CreatorID + where ""ID"" = :ID; + ", + dbcon)) + { + // create unix epoch time + int now = (int)Utils.DateTimeToUnixTime(DateTime.UtcNow); + cmd.Parameters.AddWithValue("ID", asset.ID); + cmd.Parameters.AddWithValue("Hash", hash); + cmd.Parameters.AddWithValue("Name", assetName); + cmd.Parameters.AddWithValue("Description", assetDescription); + cmd.Parameters.AddWithValue("AssetType", asset.Type); + cmd.Parameters.AddWithValue("Local", asset.Local); + cmd.Parameters.AddWithValue("Temporary", asset.Temporary); + cmd.Parameters.AddWithValue("CreateTime", now); + cmd.Parameters.AddWithValue("AccessTime", now); + cmd.Parameters.AddWithValue("CreatorID", asset.Metadata.CreatorID); + cmd.Parameters.AddWithValue("AssetFlags", (int)asset.Flags); + cmd.ExecuteNonQuery(); + } + } + catch (Exception e) + { + m_log.ErrorFormat("[ASSET DB]: PGSQL failure creating asset metadata {0} with name \"{1}\". Error: {2}", + asset.FullID, asset.Name, e.Message); + + transaction.Rollback(); + + return; + } + + if (!ExistsData(dbcon, transaction, hash)) + { + try + { + using (NpgsqlCommand cmd = + new NpgsqlCommand( + @"INSERT INTO XAssetsData(""Hash"", ""Data"") VALUES(:Hash, :Data)", + dbcon)) + { + cmd.Parameters.AddWithValue("Hash", hash); + cmd.Parameters.AddWithValue("Data", asset.Data); + cmd.ExecuteNonQuery(); + } + } + catch (Exception e) + { + m_log.ErrorFormat("[XASSET DB]: PGSQL failure creating asset data {0} with name \"{1}\". Error: {2}", + asset.FullID, asset.Name, e.Message); + + transaction.Rollback(); + + return; + } + } + + transaction.Commit(); + } + } + } + } + + /// + /// Updates the access time of the asset if it was accessed above a given threshhold amount of time. + /// + /// + /// This gives us some insight into assets which haven't ben accessed for a long period. This is only done + /// over the threshold time to avoid excessive database writes as assets are fetched. + /// + /// + /// + private void UpdateAccessTime(AssetMetadata assetMetadata, int accessTime) + { + DateTime now = DateTime.UtcNow; + + if ((now - Utils.UnixTimeToDateTime(accessTime)).TotalDays < DaysBetweenAccessTimeUpdates) + return; + + lock (m_dbLock) + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(m_connectionString)) + { + dbcon.Open(); + NpgsqlCommand cmd = + new NpgsqlCommand(@"update XAssetsMeta set ""AccessTime""=:AccessTime where ID=:ID", dbcon); + + try + { + using (cmd) + { + // create unix epoch time + cmd.Parameters.AddWithValue("ID", assetMetadata.ID); + cmd.Parameters.AddWithValue("AccessTime", (int)Utils.DateTimeToUnixTime(now)); + cmd.ExecuteNonQuery(); + } + } + catch (Exception e) + { + m_log.ErrorFormat( + "[XASSET PGSQL DB]: Failure updating access_time for asset {0} with name {1} : {2}", + assetMetadata.ID, assetMetadata.Name, e.Message); + } + } + } + } + + /// + /// We assume we already have the m_dbLock. + /// + /// TODO: need to actually use the transaction. + /// + /// + /// + /// + private bool ExistsData(NpgsqlConnection dbcon, NpgsqlTransaction transaction, byte[] hash) + { +// m_log.DebugFormat("[ASSETS DB]: Checking for asset {0}", uuid); + + bool exists = false; + + using (NpgsqlCommand cmd = new NpgsqlCommand(@"SELECT ""Hash"" FROM XAssetsData WHERE ""Hash""=:Hash", dbcon)) + { + cmd.Parameters.AddWithValue("Hash", hash); + + try + { + using (NpgsqlDataReader dbReader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if (dbReader.Read()) + { +// m_log.DebugFormat("[ASSETS DB]: Found asset {0}", uuid); + exists = true; + } + } + } + catch (Exception e) + { + m_log.ErrorFormat( + "[XASSETS DB]: PGSql failure in ExistsData fetching hash {0}. Exception {1}{2}", + hash, e.Message, e.StackTrace); + } + } + + return exists; + } + + /// + /// Check if the asset exists in the database + /// + /// The asset UUID + /// true if it exists, false otherwise. + public bool ExistsAsset(UUID uuid) + { +// m_log.DebugFormat("[ASSETS DB]: Checking for asset {0}", uuid); + + bool assetExists = false; + + lock (m_dbLock) + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(m_connectionString)) + { + dbcon.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(@"SELECT ""ID"" FROM XAssetsMeta WHERE ""ID""=:ID", dbcon)) + { + cmd.Parameters.AddWithValue("ID", uuid.ToString()); + + try + { + using (NpgsqlDataReader dbReader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if (dbReader.Read()) + { +// m_log.DebugFormat("[ASSETS DB]: Found asset {0}", uuid); + assetExists = true; + } + } + } + catch (Exception e) + { + m_log.Error(string.Format("[XASSETS DB]: PGSql failure fetching asset {0}", uuid), e); + } + } + } + } + + return assetExists; + } + + + /// + /// Returns a list of AssetMetadata objects. The list is a subset of + /// the entire data set offset by containing + /// elements. + /// + /// The number of results to discard from the total data set. + /// The number of rows the returned list should contain. + /// A list of AssetMetadata objects. + public List FetchAssetMetadataSet(int start, int count) + { + List retList = new List(count); + + lock (m_dbLock) + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(m_connectionString)) + { + dbcon.Open(); + NpgsqlCommand cmd = new NpgsqlCommand( @"SELECT ""Name"", ""Description"", ""AccessTime"", ""AssetType"", ""Temporary"", ""ID"", ""AssetFlags"", ""CreatorID"" + FROM XAssetsMeta + LIMIT :start, :count", dbcon); + cmd.Parameters.AddWithValue("start", start); + cmd.Parameters.AddWithValue("count", count); + + try + { + using (NpgsqlDataReader dbReader = cmd.ExecuteReader()) + { + while (dbReader.Read()) + { + AssetMetadata metadata = new AssetMetadata(); + metadata.Name = (string)dbReader["Name"]; + metadata.Description = (string)dbReader["Description"]; + metadata.Type = (sbyte)dbReader["AssetType"]; + metadata.Temporary = Convert.ToBoolean(dbReader["Temporary"]); // Not sure if this is correct. + metadata.Flags = (AssetFlags)Convert.ToInt32(dbReader["AssetFlags"]); + metadata.FullID = DBGuid.FromDB(dbReader["ID"]); + metadata.CreatorID = dbReader["CreatorID"].ToString(); + + // We'll ignore this for now - it appears unused! +// metadata.SHA1 = dbReader["hash"]); + + UpdateAccessTime(metadata, (int)dbReader["AccessTime"]); + + retList.Add(metadata); + } + } + } + catch (Exception e) + { + m_log.Error("[XASSETS DB]: PGSql failure fetching asset set" + Environment.NewLine + e.ToString()); + } + } + } + + return retList; + } + + public bool Delete(string id) + { +// m_log.DebugFormat("[XASSETS DB]: Deleting asset {0}", id); + + lock (m_dbLock) + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (NpgsqlCommand cmd = new NpgsqlCommand(@"delete from XAssetsMeta where ""ID""=:ID", dbcon)) + { + cmd.Parameters.AddWithValue("ID", id); + cmd.ExecuteNonQuery(); + } + + // TODO: How do we deal with data from deleted assets? Probably not easily reapable unless we + // keep a reference count (?) + } + } + + return true; + } + + #endregion + } +} diff --git a/OpenSim/Data/PGSQL/PGSQLXInventoryData.cs b/OpenSim/Data/PGSQL/PGSQLXInventoryData.cs new file mode 100644 index 0000000000..a22b882c01 --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLXInventoryData.cs @@ -0,0 +1,330 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Data; +using OpenMetaverse; +using OpenSim.Framework; +using System.Reflection; +using System.Text; +using log4net; +using Npgsql; +using NpgsqlTypes; + +namespace OpenSim.Data.PGSQL +{ + public class PGSQLXInventoryData : IXInventoryData + { +// private static readonly ILog m_log = LogManager.GetLogger( +// MethodBase.GetCurrentMethod().DeclaringType); + + private PGSQLFolderHandler m_Folders; + private PGSQLItemHandler m_Items; + + public PGSQLXInventoryData(string conn, string realm) + { + m_Folders = new PGSQLFolderHandler( + conn, "inventoryfolders", "InventoryStore"); + m_Items = new PGSQLItemHandler( + conn, "inventoryitems", String.Empty); + } + + public static UUID str2UUID(string strUUID) + { + UUID newUUID = UUID.Zero; + + UUID.TryParse(strUUID, out newUUID); + + return newUUID; + } + + public XInventoryFolder[] GetFolders(string[] fields, string[] vals) + { + return m_Folders.Get(fields, vals); + } + + public XInventoryItem[] GetItems(string[] fields, string[] vals) + { + return m_Items.Get(fields, vals); + } + + public bool StoreFolder(XInventoryFolder folder) + { + if (folder.folderName.Length > 64) + folder.folderName = folder.folderName.Substring(0, 64); + return m_Folders.Store(folder); + } + + public bool StoreItem(XInventoryItem item) + { + if (item.inventoryName.Length > 64) + item.inventoryName = item.inventoryName.Substring(0, 64); + if (item.inventoryDescription.Length > 128) + item.inventoryDescription = item.inventoryDescription.Substring(0, 128); + + return m_Items.Store(item); + } + + public bool DeleteFolders(string field, string val) + { + return m_Folders.Delete(field, val); + } + + public bool DeleteFolders(string[] fields, string[] vals) + { + return m_Folders.Delete(fields, vals); + } + + public bool DeleteItems(string field, string val) + { + return m_Items.Delete(field, val); + } + + public bool DeleteItems(string[] fields, string[] vals) + { + return m_Items.Delete(fields, vals); + } + + public bool MoveItem(string id, string newParent) + { + return m_Items.MoveItem(id, newParent); + } + + public bool MoveFolder(string id, string newParent) + { + return m_Folders.MoveFolder(id, newParent); + } + + public XInventoryItem[] GetActiveGestures(UUID principalID) + { + return m_Items.GetActiveGestures(principalID.ToString()); + } + + public int GetAssetPermissions(UUID principalID, UUID assetID) + { + return m_Items.GetAssetPermissions(principalID, assetID); + } + } + + public class PGSQLItemHandler : PGSQLInventoryHandler + { + public PGSQLItemHandler(string c, string t, string m) : + base(c, t, m) + { + } + + public bool MoveItem(string id, string newParent) + { + XInventoryItem[] retrievedItems = Get(new string[] { "inventoryID" }, new string[] { id }); + if (retrievedItems.Length == 0) + return false; + + UUID oldParent = retrievedItems[0].parentFolderID; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + { + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + cmd.CommandText = String.Format(@"update {0} set ""parentFolderID"" = :ParentFolderID where ""inventoryID"" = :InventoryID", m_Realm); + cmd.Parameters.Add(m_database.CreateParameter("ParentFolderID", newParent)); + cmd.Parameters.Add(m_database.CreateParameter("InventoryID", id )); + cmd.Connection = conn; + conn.Open(); + + if (cmd.ExecuteNonQuery() == 0) + return false; + } + } + + IncrementFolderVersion(oldParent); + IncrementFolderVersion(newParent); + + return true; + } + + public XInventoryItem[] GetActiveGestures(string principalID) + { + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + { + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + cmd.CommandText = String.Format(@"select * from inventoryitems where ""avatarID"" = :uuid and ""assetType"" = :type and ""flags"" = 1", m_Realm); + + UUID princID = UUID.Zero; + UUID.TryParse(principalID, out princID); + + cmd.Parameters.Add(m_database.CreateParameter("uuid", principalID)); + cmd.Parameters.Add(m_database.CreateParameter("type", (int)AssetType.Gesture)); + cmd.Connection = conn; + conn.Open(); + return DoQuery(cmd); + } + } + } + + public int GetAssetPermissions(UUID principalID, UUID assetID) + { + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + { + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + cmd.CommandText = String.Format(@"select bit_or(""inventoryCurrentPermissions"") as ""inventoryCurrentPermissions"" + from inventoryitems + where ""avatarID"" = :PrincipalID + and ""assetID"" = :AssetID + group by ""assetID"" ", m_Realm); + + cmd.Parameters.Add(m_database.CreateParameter("PrincipalID", principalID)); + cmd.Parameters.Add(m_database.CreateParameter("AssetID", assetID)); + cmd.Connection = conn; + conn.Open(); + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + + int perms = 0; + + if (reader.Read()) + { + perms = Convert.ToInt32(reader["inventoryCurrentPermissions"]); + } + + return perms; + } + + } + } + } + + public override bool Store(XInventoryItem item) + { + if (!base.Store(item)) + return false; + + IncrementFolderVersion(item.parentFolderID); + + return true; + } + } + + public class PGSQLFolderHandler : PGSQLInventoryHandler + { + public PGSQLFolderHandler(string c, string t, string m) : + base(c, t, m) + { + } + + public bool MoveFolder(string id, string newParentFolderID) + { + XInventoryFolder[] folders = Get(new string[] { "folderID" }, new string[] { id }); + + if (folders.Length == 0) + return false; + + UUID oldParentFolderUUID = folders[0].parentFolderID; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + { + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + UUID foldID = UUID.Zero; + UUID.TryParse(id, out foldID); + + UUID newPar = UUID.Zero; + UUID.TryParse(newParentFolderID, out newPar); + + cmd.CommandText = String.Format(@"update {0} set ""parentFolderID"" = :ParentFolderID where ""folderID"" = :folderID", m_Realm); + cmd.Parameters.Add(m_database.CreateParameter("ParentFolderID", newPar)); + cmd.Parameters.Add(m_database.CreateParameter("folderID", foldID)); + cmd.Connection = conn; + conn.Open(); + + if (cmd.ExecuteNonQuery() == 0) + return false; + } + } + + IncrementFolderVersion(oldParentFolderUUID); + IncrementFolderVersion(newParentFolderID); + + return true; + } + + public override bool Store(XInventoryFolder folder) + { + if (!base.Store(folder)) + return false; + + IncrementFolderVersion(folder.parentFolderID); + + return true; + } + } + + public class PGSQLInventoryHandler : PGSQLGenericTableHandler where T: class, new() + { + public PGSQLInventoryHandler(string c, string t, string m) : base(c, t, m) {} + + protected bool IncrementFolderVersion(UUID folderID) + { + return IncrementFolderVersion(folderID.ToString()); + } + + protected bool IncrementFolderVersion(string folderID) + { +// m_log.DebugFormat("[PGSQL ITEM HANDLER]: Incrementing version on folder {0}", folderID); +// Util.PrintCallStack(); + + string sql = @"update inventoryfolders set version=version+1 where ""folderID"" = :folderID"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + { + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + UUID foldID = UUID.Zero; + UUID.TryParse(folderID, out foldID); + + conn.Open(); + + cmd.Parameters.Add( m_database.CreateParameter("folderID", foldID) ); + + try + { + cmd.ExecuteNonQuery(); + } + catch (Exception) + { + return false; + } + } + } + + return true; + } + } +} diff --git a/OpenSim/Data/PGSQL/Properties/AssemblyInfo.cs b/OpenSim/Data/PGSQL/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..27d06790ea --- /dev/null +++ b/OpenSim/Data/PGSQL/Properties/AssemblyInfo.cs @@ -0,0 +1,65 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System.Reflection; +using System.Runtime.InteropServices; + +// General information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. + +[assembly : AssemblyTitle("OpenSim.Data.PGSQL")] +[assembly : AssemblyDescription("")] +[assembly : AssemblyConfiguration("")] +[assembly : AssemblyCompany("http://opensimulator.org")] +[assembly : AssemblyProduct("OpenSim.Data.PGSQL")] +[assembly : AssemblyCopyright("Copyright (c) OpenSimulator.org Developers 2007-2009")] +[assembly : AssemblyTrademark("")] +[assembly : AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. + +[assembly : ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM + +[assembly : Guid("0e1c1ca4-2cf2-4315-b0e7-432c02feea8a")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly : AssemblyVersion("0.7.6.*")] + diff --git a/OpenSim/Data/PGSQL/Resources/AssetStore.migrations b/OpenSim/Data/PGSQL/Resources/AssetStore.migrations new file mode 100644 index 0000000000..b6db58595b --- /dev/null +++ b/OpenSim/Data/PGSQL/Resources/AssetStore.migrations @@ -0,0 +1,94 @@ +:VERSION 1 + +CREATE TABLE assets ( + "id" varchar(36) NOT NULL PRIMARY KEY, + "name" varchar(64) NOT NULL, + "description" varchar(64) NOT NULL, + "assetType" smallint NOT NULL, + "local" smallint NOT NULL, + "temporary" smallint NOT NULL, + "data" bytea NOT NULL +) ; + +:VERSION 2 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_assets + ( + "id" varchar(36) NOT NULL, + "name" varchar(64) NOT NULL, + "description" varchar(64) NOT NULL, + "assetType" smallint NOT NULL, + "local" boolean NOT NULL, + "temporary" boolean NOT NULL, + "data" bytea NOT NULL + ) ; + +INSERT INTO Tmp_assets ("id", "name", "description", "assetType", "local", "temporary", "data") + SELECT "id", "name", "description", "assetType", case when "local" = 1 then true else false end, case when "temporary" = 1 then true else false end, "data" + FROM assets ; + +DROP TABLE assets; + +Alter table Tmp_assets + rename to assets; + +ALTER TABLE assets ADD PRIMARY KEY ("id"); + +COMMIT; + + +:VERSION 3 + +BEGIN TRANSACTION; + +ALTER TABLE assets add "create_time" integer default 0; +ALTER TABLE assets add "access_time" integer default 0; + +COMMIT; + + +:VERSION 4 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_assets + ( + "id" uuid NOT NULL, + "name" varchar(64) NOT NULL, + "description" varchar(64) NOT NULL, + "assetType" smallint NOT NULL, + "local" boolean NOT NULL, + "temporary" boolean NOT NULL, + "data" bytea NOT NULL, + "create_time" int NULL, + "access_time" int NULL + ) ; + + +INSERT INTO Tmp_assets ("id", "name", "description", "assetType", "local", "temporary", "data", "create_time", "access_time") + SELECT cast("id" as uuid), "name", "description", "assetType", "local", "temporary", "data", "create_time", "access_time" + FROM assets ; + +DROP TABLE assets; + +Alter table Tmp_assets + rename to assets; + + ALTER TABLE assets ADD PRIMARY KEY ("id"); + +COMMIT; + + +:VERSION 5 + +DELETE FROM assets WHERE "id" = 'dc4b9f0b-d008-45c6-96a4-01dd947ac621'; + +:VERSION 6 + +ALTER TABLE assets ADD "asset_flags" INTEGER NOT NULL DEFAULT 0; + +:VERSION 7 + +alter table assets add "creatorid" varchar(36) not null default ''; diff --git a/OpenSim/Data/PGSQL/Resources/AuthStore.migrations b/OpenSim/Data/PGSQL/Resources/AuthStore.migrations new file mode 100644 index 0000000000..a1f5b61ef6 --- /dev/null +++ b/OpenSim/Data/PGSQL/Resources/AuthStore.migrations @@ -0,0 +1,32 @@ +:VERSION 1 + +BEGIN TRANSACTION; + +CREATE TABLE auth ( + uuid uuid NOT NULL default '00000000-0000-0000-0000-000000000000', + "passwordHash" varchar(32) NOT NULL, + "passwordSalt" varchar(32) NOT NULL, + "webLoginKey" varchar(255) NOT NULL, + "accountType" VARCHAR(32) NOT NULL DEFAULT 'UserAccount' +) ; + +CREATE TABLE tokens ( + uuid uuid NOT NULL default '00000000-0000-0000-0000-000000000000', + token varchar(255) NOT NULL, + validity TIMESTAMP NOT NULL ) + ; + +COMMIT; + +:VERSION 2 + +BEGIN TRANSACTION; + + INSERT INTO auth (uuid, "passwordHash", "passwordSalt", "webLoginKey", "accountType") + SELECT uuid AS UUID, passwordHash AS passwordHash, passwordSalt AS passwordSalt, webLoginKey AS webLoginKey, 'UserAccount' as accountType + FROM users + where exists ( Select * from information_schema.tables where table_name = 'users' ) + ; + +COMMIT; + diff --git a/OpenSim/Data/PGSQL/Resources/Avatar.migrations b/OpenSim/Data/PGSQL/Resources/Avatar.migrations new file mode 100644 index 0000000000..160086d77c --- /dev/null +++ b/OpenSim/Data/PGSQL/Resources/Avatar.migrations @@ -0,0 +1,59 @@ +:VERSION 1 + +BEGIN TRANSACTION; + +CREATE TABLE Avatars ( +"PrincipalID" uuid NOT NULL PRIMARY KEY, +"Name" varchar(32) NOT NULL, +"Value" varchar(255) NOT NULL DEFAULT '' +); + + +COMMIT; + +:VERSION 2 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_Avatars + ( + "PrincipalID" uuid NOT NULL, + "Name" varchar(32) NOT NULL, + "Value" text NOT NULL DEFAULT '' + ) ; + + INSERT INTO Tmp_Avatars ("PrincipalID", "Name", "Value") + SELECT "PrincipalID", cast("Name" as text), "Value" + FROM Avatars ; + +DROP TABLE Avatars; + +Alter table Tmp_Avatars + rename to Avatars; + +COMMIT; + +:VERSION 3 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_Avatars + ( + "PrincipalID" uuid NOT NULL, + "Name" varchar(32) NOT NULL, + "Value" text NOT NULL DEFAULT '' +); + +ALTER TABLE Tmp_Avatars ADD PRIMARY KEY ("PrincipalID", "Name"); + + +INSERT INTO Tmp_Avatars ("PrincipalID", "Name", "Value") + SELECT "PrincipalID", "Name", cast("Value" as text) FROM Avatars ; + +DROP TABLE Avatars; + +Alter table Tmp_Avatars + rename to Avatars; + +COMMIT; + diff --git a/OpenSim/Data/PGSQL/Resources/EstateStore.migrations b/OpenSim/Data/PGSQL/Resources/EstateStore.migrations new file mode 100644 index 0000000000..59270f8b01 --- /dev/null +++ b/OpenSim/Data/PGSQL/Resources/EstateStore.migrations @@ -0,0 +1,307 @@ +:VERSION 1 + +BEGIN TRANSACTION; + +CREATE TABLE estate_managers( + "EstateID" int NOT NULL Primary Key, + uuid varchar(36) NOT NULL + ); + +CREATE TABLE estate_groups( + "EstateID" int NOT NULL, + uuid varchar(36) NOT NULL + ); + + +CREATE TABLE estate_users( + "EstateID" int NOT NULL, + uuid varchar(36) NOT NULL + ); + + +CREATE TABLE estateban( + "EstateID" int NOT NULL, + "bannedUUID" varchar(36) NOT NULL, + "bannedIp" varchar(16) NOT NULL, + "bannedIpHostMask" varchar(16) NOT NULL, + "bannedNameMask" varchar(64) NULL DEFAULT NULL + ); + +Create Sequence estate_settings_id increment by 100 start with 100; + +CREATE TABLE estate_settings( + "EstateID" integer DEFAULT nextval('estate_settings_id') NOT NULL, + "EstateName" varchar(64) NULL DEFAULT (NULL), + "AbuseEmailToEstateOwner" boolean NOT NULL, + "DenyAnonymous" boolean NOT NULL, + "ResetHomeOnTeleport" boolean NOT NULL, + "FixedSun" boolean NOT NULL, + "DenyTransacted" boolean NOT NULL, + "BlockDwell" boolean NOT NULL, + "DenyIdentified" boolean NOT NULL, + "AllowVoice" boolean NOT NULL, + "UseGlobalTime" boolean NOT NULL, + "PricePerMeter" int NOT NULL, + "TaxFree" boolean NOT NULL, + "AllowDirectTeleport" boolean NOT NULL, + "RedirectGridX" int NOT NULL, + "RedirectGridY" int NOT NULL, + "ParentEstateID" int NOT NULL, + "SunPosition" double precision NOT NULL, + "EstateSkipScripts" boolean NOT NULL, + "BillableFactor" double precision NOT NULL, + "PublicAccess" boolean NOT NULL, + "AbuseEmail" varchar(255) NOT NULL, + "EstateOwner" varchar(36) NOT NULL, + "DenyMinors" boolean NOT NULL + ); + + +CREATE TABLE estate_map( + "RegionID" varchar(36) NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + "EstateID" int NOT NULL + ); + +COMMIT; + +:VERSION 2 + +BEGIN TRANSACTION; + +CREATE INDEX IX_estate_managers ON estate_managers + ( + "EstateID" + ); + + +CREATE INDEX IX_estate_groups ON estate_groups + ( + "EstateID" + ); + + +CREATE INDEX IX_estate_users ON estate_users + ( + "EstateID" + ); + +COMMIT; + +:VERSION 3 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_estateban + ( + "EstateID" int NOT NULL, + "bannedUUID" varchar(36) NOT NULL, + "bannedIp" varchar(16) NULL, + "bannedIpHostMask" varchar(16) NULL, + "bannedNameMask" varchar(64) NULL + ); + + INSERT INTO Tmp_estateban ("EstateID", "bannedUUID", "bannedIp", "bannedIpHostMask", "bannedNameMask") + SELECT "EstateID", "bannedUUID", "bannedIp", "bannedIpHostMask", "bannedNameMask" FROM estateban; + +DROP TABLE estateban; + +Alter table Tmp_estateban + rename to estateban; + +CREATE INDEX IX_estateban ON estateban + ( + "EstateID" + ); + +COMMIT; + + +:VERSION 4 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_estate_managers + ( + "EstateID" int NOT NULL, + uuid uuid NOT NULL + ); + +INSERT INTO Tmp_estate_managers ("EstateID", uuid) + SELECT "EstateID", cast(uuid as uuid) FROM estate_managers; + +DROP TABLE estate_managers; + +Alter table Tmp_estate_managers + rename to estate_managers; + +CREATE INDEX IX_estate_managers ON estate_managers + ( + "EstateID" + ); + +COMMIT; + + +:VERSION 5 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_estate_groups + ( + "EstateID" int NOT NULL, + uuid uuid NOT NULL + ) ; + + INSERT INTO Tmp_estate_groups ("EstateID", uuid) + SELECT "EstateID", cast(uuid as uuid) FROM estate_groups; + +DROP TABLE estate_groups; + +Alter table Tmp_estate_groups + rename to estate_groups; + +CREATE INDEX IX_estate_groups ON estate_groups + ( + "EstateID" + ); + +COMMIT; + + +:VERSION 6 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_estate_users + ( + "EstateID" int NOT NULL, + uuid uuid NOT NULL + ); + +INSERT INTO Tmp_estate_users ("EstateID", uuid) + SELECT "EstateID", cast(uuid as uuid) FROM estate_users ; + +DROP TABLE estate_users; + +Alter table Tmp_estate_users + rename to estate_users; + +CREATE INDEX IX_estate_users ON estate_users + ( + "EstateID" + ); + +COMMIT; + + +:VERSION 7 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_estateban + ( + "EstateID" int NOT NULL, + "bannedUUID" uuid NOT NULL, + "bannedIp" varchar(16) NULL, + "bannedIpHostMask" varchar(16) NULL, + "bannedNameMask" varchar(64) NULL + ); + +INSERT INTO Tmp_estateban ("EstateID", "bannedUUID", "bannedIp", "bannedIpHostMask", "bannedNameMask") + SELECT "EstateID", cast("bannedUUID" as uuid), "bannedIp", "bannedIpHostMask", "bannedNameMask" FROM estateban ; + +DROP TABLE estateban; + +Alter table Tmp_estateban + rename to estateban; + +CREATE INDEX IX_estateban ON estateban + ( + "EstateID" + ); + +COMMIT; + + +:VERSION 8 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_estate_settings + ( + "EstateID" integer default nextval('estate_settings_id') NOT NULL, + "EstateName" varchar(64) NULL DEFAULT (NULL), + "AbuseEmailToEstateOwner" boolean NOT NULL, + "DenyAnonymous" boolean NOT NULL, + "ResetHomeOnTeleport" boolean NOT NULL, + "FixedSun" boolean NOT NULL, + "DenyTransacted" boolean NOT NULL, + "BlockDwell" boolean NOT NULL, + "DenyIdentified" boolean NOT NULL, + "AllowVoice" boolean NOT NULL, + "UseGlobalTime" boolean NOT NULL, + "PricePerMeter" int NOT NULL, + "TaxFree" boolean NOT NULL, + "AllowDirectTeleport" boolean NOT NULL, + "RedirectGridX" int NOT NULL, + "RedirectGridY" int NOT NULL, + "ParentEstateID" int NOT NULL, + "SunPosition" double precision NOT NULL, + "EstateSkipScripts" boolean NOT NULL, + "BillableFactor" double precision NOT NULL, + "PublicAccess" boolean NOT NULL, + "AbuseEmail" varchar(255) NOT NULL, + "EstateOwner" uuid NOT NULL, + "DenyMinors" boolean NOT NULL + ); + +INSERT INTO Tmp_estate_settings ("EstateID", "EstateName", "AbuseEmailToEstateOwner", "DenyAnonymous", "ResetHomeOnTeleport", "FixedSun", "DenyTransacted", "BlockDwell", "DenyIdentified", "AllowVoice", "UseGlobalTime", "PricePerMeter", "TaxFree", "AllowDirectTeleport", "RedirectGridX", "RedirectGridY", "ParentEstateID", "SunPosition", "EstateSkipScripts", "BillableFactor", "PublicAccess", "AbuseEmail", "EstateOwner", "DenyMinors") + SELECT "EstateID", "EstateName", "AbuseEmailToEstateOwner", "DenyAnonymous", "ResetHomeOnTeleport", "FixedSun", "DenyTransacted", "BlockDwell", "DenyIdentified", "AllowVoice", "UseGlobalTime", "PricePerMeter", "TaxFree", "AllowDirectTeleport", "RedirectGridX", "RedirectGridY", "ParentEstateID", "SunPosition", "EstateSkipScripts", "BillableFactor", "PublicAccess", "AbuseEmail", cast("EstateOwner" as uuid), "DenyMinors" FROM estate_settings ; + +DROP TABLE estate_settings; + + +Alter table Tmp_estate_settings + rename to estate_settings; + + +Create index on estate_settings (lower("EstateName")); + +COMMIT; + + +:VERSION 9 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_estate_map + ( + "RegionID" uuid NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + "EstateID" int NOT NULL + ); + +INSERT INTO Tmp_estate_map ("RegionID", "EstateID") + SELECT cast("RegionID" as uuid), "EstateID" FROM estate_map ; + +DROP TABLE estate_map; + +Alter table Tmp_estate_map + rename to estate_map; + +COMMIT; + +:VERSION 10 + +BEGIN TRANSACTION; +ALTER TABLE estate_settings ADD COLUMN "AllowLandmark" boolean NOT NULL default true; +ALTER TABLE estate_settings ADD COLUMN "AllowParcelChanges" boolean NOT NULL default true; +ALTER TABLE estate_settings ADD COLUMN "AllowSetHome" boolean NOT NULL default true; +COMMIT; + +:VERSION 11 + +Begin transaction; + + +Commit; + diff --git a/OpenSim/Data/PGSQL/Resources/FriendsStore.migrations b/OpenSim/Data/PGSQL/Resources/FriendsStore.migrations new file mode 100644 index 0000000000..a87199bfcf --- /dev/null +++ b/OpenSim/Data/PGSQL/Resources/FriendsStore.migrations @@ -0,0 +1,44 @@ +:VERSION 1 + +BEGIN TRANSACTION; + +CREATE TABLE Friends ( +"PrincipalID" uuid NOT NULL, +"Friend" varchar(255) NOT NULL, +"Flags" char(16) NOT NULL DEFAULT '0', +"Offered" varchar(32) NOT NULL DEFAULT 0); + + +COMMIT; + +:VERSION 2 + +BEGIN TRANSACTION; + +INSERT INTO Friends ("PrincipalID", "Friend", "Flags", "Offered") +SELECT "ownerID", "friendID", "friendPerms", 0 FROM userfriends; + +COMMIT; + +:VERSION 3 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_Friends + ("PrincipalID" varchar(255) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', + "Friend" varchar(255) NOT NULL, + "Flags" char(16) NOT NULL DEFAULT '0', + "Offered" varchar(32) NOT NULL DEFAULT 0) ; + +INSERT INTO Tmp_Friends ("PrincipalID", "Friend", "Flags", "Offered") + SELECT cast("PrincipalID" as varchar(255)), "Friend", "Flags", "Offered" FROM Friends ; + +DROP TABLE Friends; + +Alter table Tmp_Friends + rename to Friends; + +ALTER TABLE Friends ADD PRIMARY KEY("PrincipalID", "Friend"); + + +COMMIT; diff --git a/OpenSim/Data/PGSQL/Resources/GridStore.migrations b/OpenSim/Data/PGSQL/Resources/GridStore.migrations new file mode 100644 index 0000000000..0ab8d2b6d0 --- /dev/null +++ b/OpenSim/Data/PGSQL/Resources/GridStore.migrations @@ -0,0 +1,242 @@ +:VERSION 1 + +BEGIN TRANSACTION; + +CREATE TABLE regions( + "regionHandle" varchar(255) NULL, + "regionName" varchar(255) NULL, + uuid varchar(255) NOT NULL PRIMARY KEY, + "regionRecvKey" varchar(255) NULL, + "regionSecret" varchar(255) NULL, + "regionSendKey" varchar(255) NULL, + "regionDataURI" varchar(255) NULL, + "serverIP" varchar(255) NULL, + "serverPort" varchar(255) NULL, + "serverURI" varchar(255) NULL, + "locX" varchar(255) NULL, + "locY" varchar(255) NULL, + "locZ" varchar(255) NULL, + "eastOverrideHandle" varchar(255) NULL, + "westOverrideHandle" varchar(255) NULL, + "southOverrideHandle" varchar(255) NULL, + "northOverrideHandle" varchar(255) NULL, + "regionAssetURI" varchar(255) NULL, + "regionAssetRecvKey" varchar(255) NULL, + "regionAssetSendKey" varchar(255) NULL, + "regionUserURI" varchar(255) NULL, + "regionUserRecvKey" varchar(255) NULL, + "regionUserSendKey" varchar(255) NULL, + "regionMapTexture" varchar(255) NULL, + "serverHttpPort" varchar(255) NULL, + "serverRemotingPort" varchar(255) NULL, + "owner_uuid" varchar(36) NULL +); + +COMMIT; + + +:VERSION 2 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_regions + ( + uuid varchar(36) NOT NULL, + "regionHandle" bigint NULL, + "regionName" varchar(20) NULL, + "regionRecvKey" varchar(128) NULL, + "regionSendKey" varchar(128) NULL, + "regionSecret" varchar(128) NULL, + "regionDataURI" varchar(128) NULL, + "serverIP" varchar(64) NULL, + "serverPort" int NULL, + "serverURI" varchar(255) NULL, + "locX" int NULL, + "locY" int NULL, + "locZ" int NULL, + "eastOverrideHandle" bigint NULL, + "westOverrideHandle" bigint NULL, + "southOverrideHandle" bigint NULL, + "northOverrideHandle" bigint NULL, + "regionAssetURI" varchar(255) NULL, + "regionAssetRecvKey" varchar(128) NULL, + "regionAssetSendKey" varchar(128) NULL, + "regionUserURI" varchar(255) NULL, + "regionUserRecvKey" varchar(128) NULL, + "regionUserSendKey" varchar(128) NULL, + "regionMapTexture" varchar(36) NULL, + "serverHttpPort" int NULL, + "serverRemotingPort" int NULL, + "owner_uuid" varchar(36) NULL, + "originUUID" varchar(36) NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000') + ); + +INSERT INTO Tmp_regions (uuid, "regionHandle", "regionName", "regionRecvKey", "regionSendKey", "regionSecret", "regionDataURI", "serverIP", "serverPort", "serverURI", "locX", "locY", "locZ", "eastOverrideHandle", "westOverrideHandle", "southOverrideHandle", "northOverrideHandle", "regionAssetURI", "regionAssetRecvKey", "regionAssetSendKey", "regionUserURI", "regionUserRecvKey", "regionUserSendKey", "regionMapTexture", "serverHttpPort", "serverRemotingPort", "owner_uuid") + SELECT cast(uuid as varchar(36)), cast("regionHandle" as bigint), cast("regionName" as varchar(20)), cast("regionRecvKey" as varchar(128)), cast("regionSendKey" as varchar(128)), cast("regionSecret" as varchar(128)), cast("regionDataURI" as varchar(128)), cast("serverIP" as varchar(64)), cast("serverPort" as int), "serverURI", cast("locX" as int), cast("locY" as int), cast("locZ" as int), cast("eastOverrideHandle" as bigint), cast("westOverrideHandle" as bigint), + cast("southOverrideHandle" as bigint), cast("northOverrideHandle" as bigint), "regionAssetURI", cast("regionAssetRecvKey" as varchar(128)), cast("regionAssetSendKey" as varchar(128)), "regionUserURI", cast("regionUserRecvKey" as varchar(128)), cast("regionUserSendKey" as varchar(128)), cast("regionMapTexture" as varchar(36)), + cast("serverHttpPort" as int), cast("serverRemotingPort" as int), "owner_uuid" + FROM regions; + +DROP TABLE regions; + +alter table Tmp_regions + rename to regions; + +COMMIT; + +:VERSION 3 + +BEGIN TRANSACTION; + +CREATE INDEX IX_regions_name ON regions + ( + "regionName" + ); + +CREATE INDEX IX_regions_handle ON regions + ( + "regionHandle" + ); + + +CREATE INDEX IX_regions_override ON regions + ( + "eastOverrideHandle", + "westOverrideHandle", + "southOverrideHandle", + "northOverrideHandle" + ); + +COMMIT; + + +:VERSION 4 + +/* To prevent any potential data loss issues, you should review this script in detail before running it outside the cotext of the database designer.*/ +BEGIN TRANSACTION; + +CREATE TABLE Tmp_regions + ( + uuid uuid NOT NULL, + "regionHandle" bigint NULL, + "regionName" varchar(20) NULL, + "regionRecvKey" varchar(128) NULL, + "regionSendKey" varchar(128) NULL, + "regionSecret" varchar(128) NULL, + "regionDataURI" varchar(128) NULL, + "serverIP" varchar(64) NULL, + "serverPort" int NULL, + "serverURI" varchar(255) NULL, + "locX" int NULL, + "locY" int NULL, + "locZ" int NULL, + "eastOverrideHandle" bigint NULL, + "westOverrideHandle" bigint NULL, + "southOverrideHandle" bigint NULL, + "northOverrideHandle" bigint NULL, + "regionAssetURI" varchar(255) NULL, + "regionAssetRecvKey" varchar(128) NULL, + "regionAssetSendKey" varchar(128) NULL, + "regionUserURI" varchar(255) NULL, + "regionUserRecvKey" varchar(128) NULL, + "regionUserSendKey" varchar(128) NULL, + "regionMapTexture" uuid NULL, + "serverHttpPort" int NULL, + "serverRemotingPort" int NULL, + "owner_uuid" uuid NOT NULL, + "originUUID" uuid NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000') + ); + + +INSERT INTO Tmp_regions (uuid, "regionHandle", "regionName", "regionRecvKey", "regionSendKey", "regionSecret", "regionDataURI", "serverIP", "serverPort", "serverURI", "locX", "locY", "locZ", "eastOverrideHandle", "westOverrideHandle", "southOverrideHandle", "northOverrideHandle", "regionAssetURI", "regionAssetRecvKey", "regionAssetSendKey", "regionUserURI", "regionUserRecvKey", "regionUserSendKey", "regionMapTexture", "serverHttpPort", "serverRemotingPort", "owner_uuid", "originUUID") + SELECT cast(uuid as uuid), "regionHandle", "regionName", "regionRecvKey", "regionSendKey", "regionSecret", "regionDataURI", "serverIP", "serverPort", "serverURI", "locX", "locY", "locZ", "eastOverrideHandle", "westOverrideHandle", "southOverrideHandle", "northOverrideHandle", "regionAssetURI", "regionAssetRecvKey", "regionAssetSendKey", "regionUserURI", "regionUserRecvKey", "regionUserSendKey", cast("regionMapTexture" as uuid), "serverHttpPort", "serverRemotingPort", cast( "owner_uuid" as uuid), cast("originUUID" as uuid) FROM regions ; + + +DROP TABLE regions; + +alter table Tmp_regions rename to regions; + +ALTER TABLE regions ADD CONSTRAINT + PK__regions__uuid PRIMARY KEY + ( + uuid + ); + +CREATE INDEX IX_regions_name ON regions + ( + "regionName" + ); + +CREATE INDEX IX_regions_handle ON regions + ( + "regionHandle" + ); + +CREATE INDEX IX_regions_override ON regions + ( + "eastOverrideHandle", + "westOverrideHandle", + "southOverrideHandle", + "northOverrideHandle" + ); + +COMMIT; + + +:VERSION 5 + +BEGIN TRANSACTION; + +ALTER TABLE regions ADD access int default 0; + +COMMIT; + + +:VERSION 6 + +BEGIN TRANSACTION; + +ALTER TABLE regions ADD "ScopeID" uuid default '00000000-0000-0000-0000-000000000000'; +ALTER TABLE regions alter column "owner_uuid" set DEFAULT ('00000000-0000-0000-0000-000000000000'); +ALTER TABLE regions ADD "sizeX" integer not null default 0; +ALTER TABLE regions ADD "sizeY" integer not null default 0; + +COMMIT; + + +:VERSION 7 + +BEGIN TRANSACTION; + +ALTER TABLE regions ADD "flags" integer NOT NULL DEFAULT 0; +CREATE INDEX flags ON regions("flags"); +ALTER TABLE regions ADD "last_seen" integer NOT NULL DEFAULT 0; +ALTER TABLE regions ADD "PrincipalID" uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; +ALTER TABLE regions ADD "Token" varchar(255) NOT NULL DEFAULT 0; + +COMMIT; + +:VERSION 8 + +BEGIN TRANSACTION; +ALTER TABLE regions ALTER COLUMN "regionName" type VarChar(128) ; + +DROP INDEX IX_regions_name; +ALTER TABLE regions ALTER COLUMN "regionName" type VarChar(128), + ALTER COLUMN "regionName" SET NOT NULL; + +CREATE INDEX IX_regions_name ON regions + ( + "regionName" + ); + +COMMIT; + +:VERSION 9 + +BEGIN TRANSACTION; + +ALTER TABLE regions ADD "parcelMapTexture" uuid NULL; + +COMMIT; + diff --git a/OpenSim/Data/PGSQL/Resources/GridUserStore.migrations b/OpenSim/Data/PGSQL/Resources/GridUserStore.migrations new file mode 100644 index 0000000000..d37c4f6d72 --- /dev/null +++ b/OpenSim/Data/PGSQL/Resources/GridUserStore.migrations @@ -0,0 +1,60 @@ +:VERSION 1 # -------------------------- + +BEGIN TRANSACTION; + +CREATE TABLE GridUser ( + "UserID" VARCHAR(255) NOT NULL Primary Key, + "HomeRegionID" CHAR(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', + "HomePosition" CHAR(64) NOT NULL DEFAULT '<0,0,0>', + "HomeLookAt" CHAR(64) NOT NULL DEFAULT '<0,0,0>', + "LastRegionID" CHAR(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', + "LastPosition" CHAR(64) NOT NULL DEFAULT '<0,0,0>', + "LastLookAt" CHAR(64) NOT NULL DEFAULT '<0,0,0>', + "Online" CHAR(5) NOT NULL DEFAULT 'false', + "Login" CHAR(16) NOT NULL DEFAULT '0', + "Logout" CHAR(16) NOT NULL DEFAULT '0' +) ; + +COMMIT; + +:VERSION 2 # -------------------------- + +BEGIN TRANSACTION; + +CREATE TABLE GridUser_tmp ( + "UserID" VARCHAR(255) NOT NULL PRIMARY KEY, + "HomeRegionID" uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', + "HomePosition" CHAR(64) NOT NULL DEFAULT '<0,0,0>', + "HomeLookAt" CHAR(64) NOT NULL DEFAULT '<0,0,0>', + "LastRegionID" uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', + "LastPosition" CHAR(64) NOT NULL DEFAULT '<0,0,0>', + "LastLookAt" CHAR(64) NOT NULL DEFAULT '<0,0,0>', + "Online" CHAR(5) NOT NULL DEFAULT 'false', + "Login" CHAR(16) NOT NULL DEFAULT '0', + "Logout" CHAR(16) NOT NULL DEFAULT '0' + ); + +COMMIT; + + +INSERT INTO GridUser_tmp ("UserID" + ,"HomeRegionID" + ,"HomePosition" + ,"HomeLookAt" + ,"LastRegionID" + ,"LastPosition" + ,"LastLookAt" + ,"Online" + ,"Login" + ,"Logout") + SELECT "UserID", cast("HomeRegionID" as uuid), "HomePosition" ,"HomeLookAt" , cast("LastRegionID" as uuid), + "LastPosition" + ,"LastLookAt" + ,"Online" + ,"Login" + ,"Logout" FROM GridUser; + +DROP TABLE GridUser; + +alter table GridUser_tmp rename to GridUser; + diff --git a/OpenSim/Data/PGSQL/Resources/HGTravelStore.migrations b/OpenSim/Data/PGSQL/Resources/HGTravelStore.migrations new file mode 100644 index 0000000000..adf126d7c3 --- /dev/null +++ b/OpenSim/Data/PGSQL/Resources/HGTravelStore.migrations @@ -0,0 +1,17 @@ +:VERSION 1 # -------------------------- + +BEGIN; + +CREATE TABLE hg_traveling_data ( + "SessionID" VARCHAR(36) NOT NULL Primary Key, + "UserID" VARCHAR(36) NOT NULL, + "GridExternalName" VARCHAR(255) NOT NULL DEFAULT '', + "ServiceToken" VARCHAR(255) NOT NULL DEFAULT '', + "ClientIPAddress" VARCHAR(16) NOT NULL DEFAULT '', + "MyIPAddress" VARCHAR(16) NOT NULL DEFAULT '', + "TMStamp" timestamp NOT NULL default now() +); + + +COMMIT; + diff --git a/OpenSim/Data/PGSQL/Resources/IM_Store.migrations b/OpenSim/Data/PGSQL/Resources/IM_Store.migrations new file mode 100644 index 0000000000..70dc0119d5 --- /dev/null +++ b/OpenSim/Data/PGSQL/Resources/IM_Store.migrations @@ -0,0 +1,26 @@ +:VERSION 1 # -------------------------- + +BEGIN Transaction; + +Create Sequence im_offiline_id increment by 1 start with 1; + +CREATE TABLE im_offline ( + "ID" integer PRIMARY KEY NOT NULL DEFAULT nextval('im_offiline_id') , + "PrincipalID" char(36) NOT NULL default '', + "Message" text NOT NULL, + "TMStamp" timestamp NOT NULL default now() +); + +COMMIT; + +:VERSION 2 # -------------------------- + +BEGIN; + +/* +INSERT INTO `im_offline` SELECT * from `diva_im_offline`; +DROP TABLE `diva_im_offline`; +DELETE FROM `migrations` WHERE name='diva_im_Store'; +*/ + +COMMIT; diff --git a/OpenSim/Data/PGSQL/Resources/InventoryStore.migrations b/OpenSim/Data/PGSQL/Resources/InventoryStore.migrations new file mode 100644 index 0000000000..8f7982ad35 --- /dev/null +++ b/OpenSim/Data/PGSQL/Resources/InventoryStore.migrations @@ -0,0 +1,220 @@ +:VERSION 1 + +BEGIN TRANSACTION; + +CREATE TABLE inventoryfolders ( + "folderID" varchar(36) NOT NULL default '' PRIMARY KEY, + "agentID" varchar(36) default NULL, + "parentFolderID" varchar(36) default NULL, + "folderName" varchar(64) default NULL, + "type" smallint NOT NULL default 0, + "version" int NOT NULL default 0 +); + + +CREATE INDEX owner ON inventoryfolders +( + "agentID" ASC +); + +CREATE INDEX parent ON inventoryfolders +( + "parentFolderID" ASC +); + + +CREATE TABLE inventoryitems ( + "inventoryID" varchar(36) NOT NULL default '' Primary Key, + "assetID" varchar(36) default NULL, + "assetType" int default NULL, + "parentFolderID" varchar(36) default NULL, + "avatarID" varchar(36) default NULL, + "inventoryName" varchar(64) default NULL, + "inventoryDescription" varchar(128) default NULL, + "inventoryNextPermissions" int default NULL, + "inventoryCurrentPermissions" int default NULL, + "invType" int default NULL, + "creatorID" varchar(36) default NULL, + "inventoryBasePermissions" int NOT NULL default 0, + "inventoryEveryOnePermissions" int NOT NULL default 0, + "salePrice" int default NULL, + "saleType" smallint default NULL, + "creationDate" int default NULL, + "groupID" varchar(36) default NULL, + "groupOwned" boolean default NULL, + "flags" int default NULL +); + + +CREATE INDEX ii_owner ON inventoryitems +( + "avatarID" ASC +); + +CREATE INDEX ii_folder ON inventoryitems +( + "parentFolderID" ASC +); + +COMMIT; + + +:VERSION 2 + +BEGIN TRANSACTION; + +ALTER TABLE inventoryitems ADD "inventoryGroupPermissions" INTEGER NOT NULL default 0; + +COMMIT; + +:VERSION 3 + +/* To prevent any potential data loss issues, you should review this script in detail before running it outside the cotext of the database designer.*/ +BEGIN TRANSACTION; + +CREATE TABLE Tmp_inventoryfolders + ( + "folderID" uuid NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + "agentID" uuid NULL DEFAULT (NULL), + "parentFolderID" uuid NULL DEFAULT (NULL), + "folderName" varchar(64) NULL DEFAULT (NULL), + "type" smallint NOT NULL DEFAULT ((0)), + "version" int NOT NULL DEFAULT ((0)) + ); + + INSERT INTO Tmp_inventoryfolders ("folderID", "agentID", "parentFolderID", "folderName", type, version) + SELECT cast("folderID" as uuid), cast("agentID" as uuid), cast("parentFolderID" as uuid), "folderName", "type", "version" + FROM inventoryfolders; + +DROP TABLE inventoryfolders; + +alter table Tmp_inventoryfolders rename to inventoryfolders; + +ALTER TABLE inventoryfolders ADD CONSTRAINT + PK__inventor__C2FABFB3173876EA PRIMARY KEY + ( + "folderID" + ); + +CREATE INDEX owner ON inventoryfolders + ( + "agentID" + ); + +CREATE INDEX parent ON inventoryfolders + ( + "parentFolderID" + ); + +COMMIT; + + +:VERSION 4 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_inventoryitems + ( + "inventoryID" uuid NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + "assetID" uuid NULL DEFAULT (NULL), + "assetType" int NULL DEFAULT (NULL), + "parentFolderID" uuid NULL DEFAULT (NULL), + "avatarID" uuid NULL DEFAULT (NULL), + "inventoryName" varchar(64) NULL DEFAULT (NULL), + "inventoryDescription" varchar(128) NULL DEFAULT (NULL), + "inventoryNextPermissions" int NULL DEFAULT (NULL), + "inventoryCurrentPermissions" int NULL DEFAULT (NULL), + "invType" int NULL DEFAULT (NULL), + "creatorID" uuid NULL DEFAULT (NULL), + "inventoryBasePermissions" int NOT NULL DEFAULT ((0)), + "inventoryEveryOnePermissions" int NOT NULL DEFAULT ((0)), + "salePrice" int NULL DEFAULT (NULL), + "SaleType" smallint NULL DEFAULT (NULL), + "creationDate" int NULL DEFAULT (NULL), + "groupID" uuid NULL DEFAULT (NULL), + "groupOwned" boolean NULL DEFAULT (NULL), + "flags" int NULL DEFAULT (NULL), + "inventoryGroupPermissions" int NOT NULL DEFAULT ((0)) + ); + + + INSERT INTO Tmp_inventoryitems ("inventoryID", "assetID", "assetType", "parentFolderID", "avatarID", "inventoryName", "inventoryDescription", "inventoryNextPermissions", "inventoryCurrentPermissions", "invType", "creatorID", "inventoryBasePermissions", "inventoryEveryOnePermissions", "salePrice", "SaleType", "creationDate", "groupID", "groupOwned", "flags", "inventoryGroupPermissions") + SELECT cast("inventoryID" as uuid), cast("assetID" as uuid), "assetType", cast("parentFolderID" as uuid), cast("avatarID" as uuid), "inventoryName", "inventoryDescription", "inventoryNextPermissions", "inventoryCurrentPermissions", "invType", cast("creatorID" as uuid), "inventoryBasePermissions", "inventoryEveryOnePermissions", "salePrice", "SaleType", "creationDate", cast("groupID" as uuid), "groupOwned", "flags", "inventoryGroupPermissions" + FROM inventoryitems ; + +DROP TABLE inventoryitems; + +alter table Tmp_inventoryitems rename to inventoryitems; + +ALTER TABLE inventoryitems ADD CONSTRAINT + PK__inventor__C4B7BC2220C1E124 PRIMARY KEY + ( + "inventoryID" + ); + + +CREATE INDEX ii2_owner ON inventoryitems + ( + "avatarID" + ); + +CREATE INDEX ii2_folder ON inventoryitems + ( + "parentFolderID" + ); + +COMMIT; + +:VERSION 5 + + +BEGIN TRANSACTION; + +-- # Restoring defaults: +-- # NOTE: "inventoryID" does NOT need one: it's NOT NULL PK and a unique Guid must be provided every time anyway! + +alter table inventoryitems + alter column "inventoryBasePermissions" set default 0; +alter table inventoryitems + alter column "inventoryEveryOnePermissions" set default 0; +alter table inventoryitems + alter column "inventoryGroupPermissions" set default 0 ; + +COMMIT ; + +:VERSION 7 + +BEGIN TRANSACTION; + +-- # "creatorID" goes back to VARCHAR(36) (???) + +alter table inventoryitems + alter column "creatorID" type varchar(36); + +COMMIT ; + +:VERSION 8 + +ALTER TABLE inventoryitems + alter column "creatorID" set DEFAULT '00000000-0000-0000-0000-000000000000'; + + +:VERSION 9 + +BEGIN TRANSACTION; + +--# "creatorID" goes up to VARCHAR(255) + +alter table inventoryitems + alter column "creatorID" type varchar(255); + +Commit; + +:VERSION 10 + +BEGIN TRANSACTION; + +Alter table inventoryitems Rename Column "SaleType" to "saleType"; + +Commit; + diff --git a/OpenSim/Data/PGSQL/Resources/LogStore.migrations b/OpenSim/Data/PGSQL/Resources/LogStore.migrations new file mode 100644 index 0000000000..83727c6fd5 --- /dev/null +++ b/OpenSim/Data/PGSQL/Resources/LogStore.migrations @@ -0,0 +1,16 @@ +:VERSION 1 + +BEGIN TRANSACTION; + +CREATE TABLE logs ( + "logID" int NOT NULL Primary Key, + "target" varchar(36) default NULL, + "server" varchar(64) default NULL, + "method" varchar(64) default NULL, + "arguments" varchar(255) default NULL, + "priority" int default NULL, + "message" text +); + +COMMIT; + diff --git a/OpenSim/Data/PGSQL/Resources/Presence.migrations b/OpenSim/Data/PGSQL/Resources/Presence.migrations new file mode 100644 index 0000000000..684faa2717 --- /dev/null +++ b/OpenSim/Data/PGSQL/Resources/Presence.migrations @@ -0,0 +1,30 @@ +:VERSION 1 + +BEGIN TRANSACTION; + +CREATE TABLE Presence ( +"UserID" varchar(255) NOT NULL, +"RegionID" uuid NOT NULL, +"SessionID" uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', +"SecureSessionID" uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000' +); + + +COMMIT; + +:VERSION 2 + +BEGIN TRANSACTION; + +CREATE UNIQUE INDEX SessionID ON Presence("SessionID"); +CREATE INDEX UserID ON Presence("UserID"); + +COMMIT; + +:VERSION 2 + +BEGIN TRANSACTION; + +ALTER TABLE Presence ADD "LastSeen" Timestamp; + +COMMIT; diff --git a/OpenSim/Data/PGSQL/Resources/RegionStore.migrations b/OpenSim/Data/PGSQL/Resources/RegionStore.migrations new file mode 100644 index 0000000000..1e33027e48 --- /dev/null +++ b/OpenSim/Data/PGSQL/Resources/RegionStore.migrations @@ -0,0 +1,1154 @@ +begin transaction ; +:VERSION 1 + +CREATE TABLE prims( + "UUID" varchar(255) NOT NULL Primary key, + "RegionUUID" varchar(255) NULL, + "ParentID" int NULL, + "CreationDate" int NULL, + "Name" varchar(255) NULL, + "SceneGroupID" varchar(255) NULL, + "Text" varchar(255) NULL, + "Description" varchar(255) NULL, + "SitName" varchar(255) NULL, + "TouchName" varchar(255) NULL, + "ObjectFlags" int NULL, + "CreatorID" varchar(255) NULL, + "OwnerID" varchar(255) NULL, + "GroupID" varchar(255) NULL, + "LastOwnerID" varchar(255) NULL, + "OwnerMask" int NULL, + "NextOwnerMask" int NULL, + "GroupMask" int NULL, + "EveryoneMask" int NULL, + "BaseMask" int NULL, + "PositionX" double precision NULL, + "PositionY" double precision NULL, + "PositionZ" double precision NULL, + "GroupPositionX" double precision NULL, + "GroupPositionY" double precision NULL, + "GroupPositionZ" double precision NULL, + "VelocityX" double precision NULL, + "VelocityY" double precision NULL, + "VelocityZ" double precision NULL, + "AngularVelocityX" double precision NULL, + "AngularVelocityY" double precision NULL, + "AngularVelocityZ" double precision NULL, + "AccelerationX" double precision NULL, + "AccelerationY" double precision NULL, + "AccelerationZ" double precision NULL, + "RotationX" double precision NULL, + "RotationY" double precision NULL, + "RotationZ" double precision NULL, + "RotationW" double precision NULL, + "SitTargetOffsetX" double precision NULL, + "SitTargetOffsetY" double precision NULL, + "SitTargetOffsetZ" double precision NULL, + "SitTargetOrientW" double precision NULL, + "SitTargetOrientX" double precision NULL, + "SitTargetOrientY" double precision NULL, + "SitTargetOrientZ" double precision NULL + ); + +CREATE TABLE primshapes( + "UUID" varchar(255) NOT NULL primary key, + "Shape" int NULL, + "ScaleX" double precision NULL, + "ScaleY" double precision NULL, + "ScaleZ" double precision NULL, + "PCode" int NULL, + "PathBegin" int NULL, + "PathEnd" int NULL, + "PathScaleX" int NULL, + "PathScaleY" int NULL, + "PathShearX" int NULL, + "PathShearY" int NULL, + "PathSkew" int NULL, + "PathCurve" int NULL, + "PathRadiusOffset" int NULL, + "PathRevolutions" int NULL, + "PathTaperX" int NULL, + "PathTaperY" int NULL, + "PathTwist" int NULL, + "PathTwistBegin" int NULL, + "ProfileBegin" int NULL, + "ProfileEnd" int NULL, + "ProfileCurve" int NULL, + "ProfileHollow" int NULL, + "State" int NULL, + "Texture" bytea NULL, + "ExtraParams" bytea NULL + ); + +CREATE TABLE primitems( + "itemID" varchar(255) NOT NULL primary key, + "primID" varchar(255) NULL, + "assetID" varchar(255) NULL, + "parentFolderID" varchar(255) NULL, + "invType" int NULL, + "assetType" int NULL, + "name" varchar(255) NULL, + "description" varchar(255) NULL, + "creationDate" varchar(255) NULL, + "creatorID" varchar(255) NULL, + "ownerID" varchar(255) NULL, + "lastOwnerID" varchar(255) NULL, + "groupID" varchar(255) NULL, + "nextPermissions" int NULL, + "currentPermissions" int NULL, + "basePermissions" int NULL, + "everyonePermissions" int NULL, + "groupPermissions" int NULL + ); + +CREATE TABLE terrain( + "RegionUUID" varchar(255) NULL, + "Revision" int NULL, + "Heightfield" bytea NULL +); + + +CREATE TABLE land( + "UUID" varchar(255) NOT NULL primary key, + "RegionUUID" varchar(255) NULL, + "LocalLandID" int NULL, + "Bitmap" bytea NULL, + "Name" varchar(255) NULL, + "Description" varchar(255) NULL, + "OwnerUUID" varchar(255) NULL, + "IsGroupOwned" boolean NULL, + "Area" int NULL, + "AuctionID" int NULL, + "Category" int NULL, + "ClaimDate" int NULL, + "ClaimPrice" int NULL, + "GroupUUID" varchar(255) NULL, + "SalePrice" int NULL, + "LandStatus" int NULL, + "LandFlags" int NULL, + "LandingType" int NULL, + "MediaAutoScale" int NULL, + "MediaTextureUUID" varchar(255) NULL, + "MediaURL" varchar(255) NULL, + "MusicURL" varchar(255) NULL, + "PassHours" double precision NULL, + "PassPrice" int NULL, + "SnapshotUUID" varchar(255) NULL, + "UserLocationX" double precision NULL, + "UserLocationY" double precision NULL, + "UserLocationZ" double precision NULL, + "UserLookAtX" double precision NULL, + "UserLookAtY" double precision NULL, + "UserLookAtZ" double precision NULL +); + +Create index on land (lower("Name")); + +CREATE TABLE landaccesslist( + "LandUUID" varchar(255) NULL, + "AccessUUID" varchar(255) NULL, + "Flags" int NULL +); + +COMMIT; + +:VERSION 2 + +BEGIN TRANSACTION; + +CREATE TABLE regionban ( + "regionUUID" VARCHAR(36) NOT NULL, + "bannedUUID" VARCHAR(36) NOT NULL, + "bannedIp" VARCHAR(16) NOT NULL, + "bannedIpHostMask" VARCHAR(16) NOT NULL + ); + +create table regionsettings ( + "regionUUID" varchar(36) not null primary key, + "block_terraform" boolean not null, + "block_fly" boolean not null, + "allow_damage" boolean not null, + "restrict_pushing" boolean not null, + "allow_land_resell" boolean not null, + "allow_land_join_divide" boolean not null, + "block_show_in_search" boolean not null, + "agent_limit" int not null, + "object_bonus" double precision not null, + "maturity" int not null, + "disable_scripts" boolean not null, + "disable_collisions" boolean not null, + "disable_physics" boolean not null, + "terrain_texture_1" varchar(36) not null, + "terrain_texture_2" varchar(36) not null, + "terrain_texture_3" varchar(36) not null, + "terrain_texture_4" varchar(36) not null, + "elevation_1_nw" double precision not null, + "elevation_2_nw" double precision not null, + "elevation_1_ne" double precision not null, + "elevation_2_ne" double precision not null, + "elevation_1_se" double precision not null, + "elevation_2_se" double precision not null, + "elevation_1_sw" double precision not null, + "elevation_2_sw" double precision not null, + "water_height" double precision not null, + "terrain_raise_limit" double precision not null, + "terrain_lower_limit" double precision not null, + "use_estate_sun" boolean not null, + "fixed_sun" boolean not null, + "sun_position" double precision not null, + "covenant" varchar(36) default NULL, + "Sandbox" boolean NOT NULL + ); + +COMMIT; + +:VERSION 3 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_prims + ( + "UUID" varchar(36) NOT NULL , + "RegionUUID" varchar(36) NULL, + "ParentID" int NULL, + "CreationDate" int NULL, + "Name" varchar(255) NULL, + "SceneGroupID" varchar(36) NULL, + "Text" varchar(255) NULL, + "Description" varchar(255) NULL, + "SitName" varchar(255) NULL, + "TouchName" varchar(255) NULL, + "ObjectFlags" int NULL, + "CreatorID" varchar(36) NULL, + "OwnerID" varchar(36) NULL, + "GroupID" varchar(36) NULL, + "LastOwnerID" varchar(36) NULL, + "OwnerMask" int NULL, + "NextOwnerMask" int NULL, + "GroupMask" int NULL, + "EveryoneMask" int NULL, + "BaseMask" int NULL, + "PositionX" double precision NULL, + "PositionY" double precision NULL, + "PositionZ" double precision NULL, + "GroupPositionX" double precision NULL, + "GroupPositionY" double precision NULL, + "GroupPositionZ" double precision NULL, + "VelocityX" double precision NULL, + "VelocityY" double precision NULL, + "VelocityZ" double precision NULL, + "AngularVelocityX" double precision NULL, + "AngularVelocityY" double precision NULL, + "AngularVelocityZ" double precision NULL, + "AccelerationX" double precision NULL, + "AccelerationY" double precision NULL, + "AccelerationZ" double precision NULL, + "RotationX" double precision NULL, + "RotationY" double precision NULL, + "RotationZ" double precision NULL, + "RotationW" double precision NULL, + "SitTargetOffsetX" double precision NULL, + "SitTargetOffsetY" double precision NULL, + "SitTargetOffsetZ" double precision NULL, + "SitTargetOrientW" double precision NULL, + "SitTargetOrientX" double precision NULL, + "SitTargetOrientY" double precision NULL, + "SitTargetOrientZ" double precision NULL + ); + +INSERT INTO Tmp_prims ("UUID", "RegionUUID", "ParentID", "CreationDate", "Name", "SceneGroupID", "Text", "Description", "SitName", "TouchName", "ObjectFlags", "CreatorID", "OwnerID", "GroupID", "LastOwnerID", "OwnerMask", "NextOwnerMask", "GroupMask", "EveryoneMask", "BaseMask", "PositionX", "PositionY", "PositionZ", "GroupPositionX", "GroupPositionY", "GroupPositionZ", "VelocityX", "VelocityY", "VelocityZ", "AngularVelocityX", "AngularVelocityY", "AngularVelocityZ", "AccelerationX", "AccelerationY", "AccelerationZ", "RotationX", "RotationY", "RotationZ", "RotationW", "SitTargetOffsetX", "SitTargetOffsetY", "SitTargetOffsetZ", "SitTargetOrientW", "SitTargetOrientX", "SitTargetOrientY", "SitTargetOrientZ") + SELECT cast("UUID" as varchar(36)), cast("RegionUUID" as varchar(36)), "ParentID", "CreationDate", "Name", cast("SceneGroupID" as varchar(36)), "Text", "Description", "SitName", "TouchName", "ObjectFlags", cast("CreatorID" as varchar(36)), cast("OwnerID" as varchar(36)), cast( "GroupID" as varchar(36)), cast("LastOwnerID" as varchar(36)), "OwnerMask", "NextOwnerMask", "GroupMask", "EveryoneMask", "BaseMask", "PositionX", "PositionY", "PositionZ", "GroupPositionX", "GroupPositionY", "GroupPositionZ", "VelocityX", "VelocityY", "VelocityZ", "AngularVelocityX", "AngularVelocityY", "AngularVelocityZ", "AccelerationX", "AccelerationY", "AccelerationZ", "RotationX", "RotationY", "RotationZ", "RotationW", "SitTargetOffsetX", "SitTargetOffsetY", "SitTargetOffsetZ", "SitTargetOrientW", "SitTargetOrientX", "SitTargetOrientY", "SitTargetOrientZ" + FROM prims ; + +DROP TABLE prims; + +alter table Tmp_prims rename to prims; + + +ALTER TABLE prims ADD CONSTRAINT + PK__prims__10566F31 PRIMARY KEY + ( + "UUID" + ); + +COMMIT; + +:VERSION 4 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_primitems + ( + "itemID" varchar(36) NOT NULL, + "primID" varchar(36) NULL, + "assetID" varchar(36) NULL, + "parentFolderID" varchar(36) NULL, + "invType" int NULL, + "assetType" int NULL, + "name" varchar(255) NULL, + "description" varchar(255) NULL, + "creationDate" varchar(255) NULL, + "creatorID" varchar(36) NULL, + "ownerID" varchar(36) NULL, + "lastOwnerID" varchar(36) NULL, + "groupID" varchar(36) NULL, + "nextPermissions" int NULL, + "currentPermissions" int NULL, + "basePermissions" int NULL, + "everyonePermissions" int NULL, + "groupPermissions" int NULL + ); + +INSERT INTO Tmp_primitems ("itemID", "primID", "assetID", "parentFolderID", "invType", "assetType", "name", "description", "creationDate", "creatorID", "ownerID", "lastOwnerID", "groupID", "nextPermissions", "currentPermissions", "basePermissions", "everyonePermissions", "groupPermissions") + SELECT cast("itemID" as varchar(36)), cast("primID" as varchar(36)), cast("assetID" as varchar(36)), cast( "parentFolderID" as varchar(36)), "invType", "assetType", "name", "description", "creationDate", cast( "creatorID" as varchar(36)), cast("ownerID" as varchar(36)), cast("lastOwnerID" as varchar(36)), cast("groupID" as varchar(36)), "nextPermissions", "currentPermissions", "basePermissions", "everyonePermissions", "groupPermissions" + from primitems; + +DROP TABLE primitems; + +alter table Tmp_primitems rename to primitems; + +ALTER TABLE primitems ADD CONSTRAINT + PK__primitems__0A688BB1 PRIMARY KEY + ( + "itemID" + ); + + +COMMIT; + + +:VERSION 5 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_primshapes + ( + "UUID" varchar(36) NOT NULL, + "Shape" int NULL, + "ScaleX" double precision NULL, + "ScaleY" double precision NULL, + "ScaleZ" double precision NULL, + "PCode" int NULL, + "PathBegin" int NULL, + "PathEnd" int NULL, + "PathScaleX" int NULL, + "PathScaleY" int NULL, + "PathShearX" int NULL, + "PathShearY" int NULL, + "PathSkew" int NULL, + "PathCurve" int NULL, + "PathRadiusOffset" int NULL, + "PathRevolutions" int NULL, + "PathTaperX" int NULL, + "PathTaperY" int NULL, + "PathTwist" int NULL, + "PathTwistBegin" int NULL, + "ProfileBegin" int NULL, + "ProfileEnd" int NULL, + "ProfileCurve" int NULL, + "ProfileHollow" int NULL, + "State" int NULL, + "Texture" bytea NULL, + "ExtraParams" bytea NULL + ) ; + +INSERT INTO Tmp_primshapes ("UUID", "Shape", "ScaleX", "ScaleY", "ScaleZ", "PCode", "PathBegin", "PathEnd", "PathScaleX", "PathScaleY", "PathShearX", "PathShearY", "PathSkew", "PathCurve", "PathRadiusOffset", "PathRevolutions", "PathTaperX", "PathTaperY", "PathTwist", "PathTwistBegin", "ProfileBegin", "ProfileEnd", "ProfileCurve", "ProfileHollow", "State", "Texture", "ExtraParams") + SELECT cast("UUID" as varchar(36)), "Shape", "ScaleX", "ScaleY", "ScaleZ", "PCode", "PathBegin", "PathEnd", "PathScaleX", "PathScaleY", "PathShearX", "PathShearY", "PathSkew", "PathCurve", "PathRadiusOffset", "PathRevolutions", "PathTaperX", "PathTaperY", "PathTwist", "PathTwistBegin", "ProfileBegin", "ProfileEnd", "ProfileCurve", "ProfileHollow", "State", "Texture", "ExtraParams" + FROM primshapes; + +DROP TABLE primshapes; + +alter table Tmp_primshapes rename to primshapes; + +ALTER TABLE primshapes ADD CONSTRAINT + PK__primshapes__0880433F PRIMARY KEY + ( + "UUID" + ) ; + +COMMIT; + + +:VERSION 6 + +BEGIN TRANSACTION; + +ALTER TABLE prims ADD "PayPrice" int not null default 0; +ALTER TABLE prims ADD "PayButton1" int not null default 0; +ALTER TABLE prims ADD "PayButton2" int not null default 0; +ALTER TABLE prims ADD "PayButton3" int not null default 0; +ALTER TABLE prims ADD "PayButton4" int not null default 0; +ALTER TABLE prims ADD "LoopedSound" varchar(36) not null default '00000000-0000-0000-0000-000000000000'; +ALTER TABLE prims ADD "LoopedSoundGain" double precision not null default 0.0; +ALTER TABLE prims ADD "TextureAnimation" bytea; +ALTER TABLE prims ADD "OmegaX" double precision not null default 0.0; +ALTER TABLE prims ADD "OmegaY" double precision not null default 0.0; +ALTER TABLE prims ADD "OmegaZ" double precision not null default 0.0; +ALTER TABLE prims ADD "CameraEyeOffsetX" double precision not null default 0.0; +ALTER TABLE prims ADD "CameraEyeOffsetY" double precision not null default 0.0; +ALTER TABLE prims ADD "CameraEyeOffsetZ" double precision not null default 0.0; +ALTER TABLE prims ADD "CameraAtOffsetX" double precision not null default 0.0; +ALTER TABLE prims ADD "CameraAtOffsetY" double precision not null default 0.0; +ALTER TABLE prims ADD "CameraAtOffsetZ" double precision not null default 0.0; +ALTER TABLE prims ADD "ForceMouselook" smallint not null default 0; +ALTER TABLE prims ADD "ScriptAccessPin" int not null default 0; +ALTER TABLE prims ADD "AllowedDrop" smallint not null default 0; +ALTER TABLE prims ADD "DieAtEdge" smallint not null default 0; +ALTER TABLE prims ADD "SalePrice" int not null default 10; +ALTER TABLE prims ADD "SaleType" smallint not null default 0; + +ALTER TABLE primitems add "flags" integer not null default 0; + +ALTER TABLE land ADD "AuthbuyerID" varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000'; + +CREATE index prims_regionuuid on prims("RegionUUID"); +CREATE index prims_parentid on prims("ParentID"); + +CREATE index primitems_primid on primitems("primID"); + +COMMIT; + + +:VERSION 7 + +BEGIN TRANSACTION; + +ALTER TABLE prims ADD "ColorR" int not null default 0; +ALTER TABLE prims ADD "ColorG" int not null default 0; +ALTER TABLE prims ADD "ColorB" int not null default 0; +ALTER TABLE prims ADD "ColorA" int not null default 0; +ALTER TABLE prims ADD "ParticleSystem" bytea; +ALTER TABLE prims ADD "ClickAction" smallint NOT NULL default 0; + +COMMIT; + + +:VERSION 8 + +BEGIN TRANSACTION; + +ALTER TABLE land ADD "OtherCleanTime" integer NOT NULL default 0; +ALTER TABLE land ADD "Dwell" integer NOT NULL default 0; + +COMMIT; + +:VERSION 9 + +BEGIN TRANSACTION; + +ALTER TABLE prims ADD "Material" smallint NOT NULL default 3; + +COMMIT; + + +:VERSION 10 + +BEGIN TRANSACTION; + +ALTER TABLE regionsettings ADD "sunvectorx" double precision NOT NULL default 0; +ALTER TABLE regionsettings ADD "sunvectory" double precision NOT NULL default 0; +ALTER TABLE regionsettings ADD "sunvectorz" double precision NOT NULL default 0; + +COMMIT; + + +:VERSION 11 + +BEGIN TRANSACTION; + +ALTER TABLE prims ADD "CollisionSound" char(36) not null default '00000000-0000-0000-0000-000000000000'; +ALTER TABLE prims ADD "CollisionSoundVolume" double precision not null default 0.0; + +COMMIT; + + +:VERSION 12 + +BEGIN TRANSACTION; + +ALTER TABLE prims ADD "LinkNumber" integer not null default 0; + +COMMIT; + + +:VERSION 13 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_prims + ( + "UUID" uuid NOT NULL, + "RegionUUID" uuid NULL, + "ParentID" int NULL, + "CreationDate" int NULL, + "Name" varchar(255) NULL, + "SceneGroupID" uuid NULL, + "Text" varchar(255) NULL, + "Description" varchar(255) NULL, + "SitName" varchar(255) NULL, + "TouchName" varchar(255) NULL, + "ObjectFlags" int NULL, + "CreatorID" uuid NULL, + "OwnerID" uuid NULL, + "GroupID" uuid NULL, + "LastOwnerID" uuid NULL, + "OwnerMask" int NULL, + "NextOwnerMask" int NULL, + "GroupMask" int NULL, + "EveryoneMask" int NULL, + "BaseMask" int NULL, + "PositionX" double precision NULL, + "PositionY" double precision NULL, + "PositionZ" double precision NULL, + "GroupPositionX" double precision NULL, + "GroupPositionY" double precision NULL, + "GroupPositionZ" double precision NULL, + "VelocityX" double precision NULL, + "VelocityY" double precision NULL, + "VelocityZ" double precision NULL, + "AngularVelocityX" double precision NULL, + "AngularVelocityY" double precision NULL, + "AngularVelocityZ" double precision NULL, + "AccelerationX" double precision NULL, + "AccelerationY" double precision NULL, + "AccelerationZ" double precision NULL, + "RotationX" double precision NULL, + "RotationY" double precision NULL, + "RotationZ" double precision NULL, + "RotationW" double precision NULL, + "SitTargetOffsetX" double precision NULL, + "SitTargetOffsetY" double precision NULL, + "SitTargetOffsetZ" double precision NULL, + "SitTargetOrientW" double precision NULL, + "SitTargetOrientX" double precision NULL, + "SitTargetOrientY" double precision NULL, + "SitTargetOrientZ" double precision NULL, + "PayPrice" int NOT NULL DEFAULT ((0)), + "PayButton1" int NOT NULL DEFAULT ((0)), + "PayButton2" int NOT NULL DEFAULT ((0)), + "PayButton3" int NOT NULL DEFAULT ((0)), + "PayButton4" int NOT NULL DEFAULT ((0)), + "LoopedSound" uuid NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + "LoopedSoundGain" double precision NOT NULL DEFAULT ((0.0)), + "TextureAnimation" bytea NULL, + "OmegaX" double precision NOT NULL DEFAULT ((0.0)), + "OmegaY" double precision NOT NULL DEFAULT ((0.0)), + "OmegaZ" double precision NOT NULL DEFAULT ((0.0)), + "CameraEyeOffsetX" double precision NOT NULL DEFAULT ((0.0)), + "CameraEyeOffsetY" double precision NOT NULL DEFAULT ((0.0)), + "CameraEyeOffsetZ" double precision NOT NULL DEFAULT ((0.0)), + "CameraAtOffsetX" double precision NOT NULL DEFAULT ((0.0)), + "CameraAtOffsetY" double precision NOT NULL DEFAULT ((0.0)), + "CameraAtOffsetZ" double precision NOT NULL DEFAULT ((0.0)), + "ForceMouselook" smallint NOT NULL DEFAULT ((0)), + "ScriptAccessPin" int NOT NULL DEFAULT ((0)), + "AllowedDrop" smallint NOT NULL DEFAULT ((0)), + "DieAtEdge" smallint NOT NULL DEFAULT ((0)), + "SalePrice" int NOT NULL DEFAULT ((10)), + "SaleType" smallint NOT NULL DEFAULT ((0)), + "ColorR" int NOT NULL DEFAULT ((0)), + "ColorG" int NOT NULL DEFAULT ((0)), + "ColorB" int NOT NULL DEFAULT ((0)), + "ColorA" int NOT NULL DEFAULT ((0)), + "ParticleSystem" bytea NULL, + "ClickAction" smallint NOT NULL DEFAULT ((0)), + "Material" smallint NOT NULL DEFAULT ((3)), + "CollisionSound" uuid NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + "CollisionSoundVolume" double precision NOT NULL DEFAULT ((0.0)), + "LinkNumber" int NOT NULL DEFAULT ((0)) + ); + +INSERT INTO Tmp_prims ("UUID", "RegionUUID", "ParentID", "CreationDate", "Name", "SceneGroupID", "Text", "Description", "SitName", "TouchName", "ObjectFlags", "CreatorID", "OwnerID", "GroupID", "LastOwnerID", "OwnerMask", "NextOwnerMask", "GroupMask", "EveryoneMask", "BaseMask", "PositionX", "PositionY", "PositionZ", "GroupPositionX", "GroupPositionY", "GroupPositionZ", "VelocityX", "VelocityY", "VelocityZ", "AngularVelocityX", "AngularVelocityY", "AngularVelocityZ", "AccelerationX", "AccelerationY", "AccelerationZ", "RotationX", "RotationY", "RotationZ", "RotationW", "SitTargetOffsetX", "SitTargetOffsetY", "SitTargetOffsetZ", "SitTargetOrientW", "SitTargetOrientX", "SitTargetOrientY", "SitTargetOrientZ", "PayPrice", "PayButton1", "PayButton2", "PayButton3", "PayButton4", "LoopedSound", "LoopedSoundGain", "TextureAnimation", "OmegaX", "OmegaY", "OmegaZ", "CameraEyeOffsetX", "CameraEyeOffsetY", "CameraEyeOffsetZ", "CameraAtOffsetX", "CameraAtOffsetY", "CameraAtOffsetZ", "ForceMouselook", "ScriptAccessPin", "AllowedDrop", "DieAtEdge", "SalePrice", "SaleType", "ColorR", "ColorG", "ColorB", "ColorA", "ParticleSystem", "ClickAction", "Material", "CollisionSound", "CollisionSoundVolume", "LinkNumber") + SELECT cast("UUID" as uuid), cast("RegionUUID" as uuid), "ParentID", "CreationDate", "Name", cast("SceneGroupID" as uuid), "Text", "Description", "SitName", "TouchName", "ObjectFlags", cast("CreatorID" as uuid), cast("OwnerID" as uuid), cast("GroupID" as uuid), cast("LastOwnerID" as uuid), "OwnerMask", "NextOwnerMask", "GroupMask", "EveryoneMask", "BaseMask", "PositionX", "PositionY", "PositionZ", "GroupPositionX", "GroupPositionY", "GroupPositionZ", "VelocityX", "VelocityY", "VelocityZ", "AngularVelocityX", "AngularVelocityY", "AngularVelocityZ", "AccelerationX", "AccelerationY", "AccelerationZ", "RotationX", "RotationY", "RotationZ", "RotationW", "SitTargetOffsetX", "SitTargetOffsetY", "SitTargetOffsetZ", "SitTargetOrientW", "SitTargetOrientX", "SitTargetOrientY", "SitTargetOrientZ", "PayPrice", "PayButton1", "PayButton2", "PayButton3", "PayButton4", cast("LoopedSound" as uuid), "LoopedSoundGain", "TextureAnimation", "OmegaX", "OmegaY", "OmegaZ", "CameraEyeOffsetX", "CameraEyeOffsetY", "CameraEyeOffsetZ", "CameraAtOffsetX", "CameraAtOffsetY", "CameraAtOffsetZ", "ForceMouselook", "ScriptAccessPin", "AllowedDrop", "DieAtEdge", "SalePrice", "SaleType", "ColorR", "ColorG", "ColorB", "ColorA", "ParticleSystem", "ClickAction", "Material", cast("CollisionSound" as uuid), "CollisionSoundVolume", "LinkNumber" + FROM prims ; + +DROP TABLE prims; + +alter table Tmp_prims rename to prims; + +ALTER TABLE prims ADD CONSTRAINT + PK__prims__10566F31 PRIMARY KEY + ( + "UUID" + ); + + +CREATE INDEX prims_regionuuid ON prims + ( + "RegionUUID" + ); + +CREATE INDEX prims_parentid ON prims + ( + "ParentID" + ); + +COMMIT; + + +:VERSION 14 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_primshapes + ( + "UUID" uuid NOT NULL, + "Shape" int NULL, + "ScaleX" double precision NULL, + "ScaleY" double precision NULL, + "ScaleZ" double precision NULL, + "PCode" int NULL, + "PathBegin" int NULL, + "PathEnd" int NULL, + "PathScaleX" int NULL, + "PathScaleY" int NULL, + "PathShearX" int NULL, + "PathShearY" int NULL, + "PathSkew" int NULL, + "PathCurve" int NULL, + "PathRadiusOffset" int NULL, + "PathRevolutions" int NULL, + "PathTaperX" int NULL, + "PathTaperY" int NULL, + "PathTwist" int NULL, + "PathTwistBegin" int NULL, + "ProfileBegin" int NULL, + "ProfileEnd" int NULL, + "ProfileCurve" int NULL, + "ProfileHollow" int NULL, + "State" int NULL, + "Texture" bytea NULL, + "ExtraParams" bytea NULL + ); + +INSERT INTO Tmp_primshapes ("UUID", "Shape", "ScaleX", "ScaleY", "ScaleZ", "PCode", "PathBegin", "PathEnd", "PathScaleX", "PathScaleY", "PathShearX", "PathShearY", "PathSkew", "PathCurve", "PathRadiusOffset", "PathRevolutions", "PathTaperX", "PathTaperY", "PathTwist", "PathTwistBegin", "ProfileBegin", "ProfileEnd", "ProfileCurve", "ProfileHollow", "State", "Texture", "ExtraParams") + SELECT cast("UUID" as uuid), "Shape", "ScaleX", "ScaleY", "ScaleZ", "PCode", "PathBegin", "PathEnd", "PathScaleX", "PathScaleY", "PathShearX", "PathShearY", "PathSkew", "PathCurve", "PathRadiusOffset", "PathRevolutions", "PathTaperX", "PathTaperY", "PathTwist", "PathTwistBegin", "ProfileBegin", "ProfileEnd", "ProfileCurve", "ProfileHollow", "State", "Texture", "ExtraParams" + FROM primshapes; + +DROP TABLE primshapes; + +alter table Tmp_primshapes rename to primshapes; + +ALTER TABLE primshapes ADD CONSTRAINT + PK__primshapes__0880433F PRIMARY KEY + ( + "UUID" + ); + +COMMIT; + + +:VERSION 15 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_primitems + ( + "itemID" uuid NOT NULL, + "primID" uuid NULL, + "assetID" uuid NULL, + "parentFolderID" uuid NULL, + "invType" int NULL, + "assetType" int NULL, + "name" varchar(255) NULL, + "description" varchar(255) NULL, + "creationDate" varchar(255) NULL, + "creatorID" uuid NULL, + "ownerID" uuid NULL, + "lastOwnerID" uuid NULL, + "groupID" uuid NULL, + "nextPermissions" int NULL, + "currentPermissions" int NULL, + "basePermissions" int NULL, + "everyonePermissions" int NULL, + "groupPermissions" int NULL, + flags int NOT NULL DEFAULT ((0)) + ); + +INSERT INTO Tmp_primitems ("itemID", "primID", "assetID", "parentFolderID", "invType", "assetType", "name", "description", "creationDate", "creatorID", "ownerID", "lastOwnerID", "groupID", "nextPermissions", "currentPermissions", "basePermissions", "everyonePermissions", "groupPermissions", flags) + SELECT cast("itemID" as uuid), cast("primID" as uuid), cast("assetID" as uuid), cast("parentFolderID" as uuid), "invType", "assetType", "name", "description", "creationDate", cast("creatorID" as uuid), cast("ownerID" as uuid), cast("lastOwnerID" as uuid), cast("groupID" as uuid), "nextPermissions", "currentPermissions", "basePermissions", "everyonePermissions", "groupPermissions", flags + FROM primitems ; + +DROP TABLE primitems; + +alter table Tmp_primitems rename to primitems; + +ALTER TABLE primitems ADD CONSTRAINT + PK__primitems__0A688BB1 PRIMARY KEY + ( + "itemID" + ); + +CREATE INDEX primitems_primid ON primitems + ( + "primID" + ) ; + +COMMIT; + + +:VERSION 16 + + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_terrain + ( + "RegionUUID" uuid NULL, + "Revision" int NULL, + "Heightfield" bytea NULL + ); + +INSERT INTO Tmp_terrain ("RegionUUID", "Revision", "Heightfield") + SELECT cast("RegionUUID" as uuid), "Revision", "Heightfield" + FROM terrain ; + +DROP TABLE terrain; + +alter table Tmp_terrain rename to terrain; + +COMMIT; + + +:VERSION 17 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_land + ( + "UUID" uuid NOT NULL, + "RegionUUID" uuid NULL, + "LocalLandID" int NULL, + "Bitmap" bytea NULL, + "Name" varchar(255) NULL, + "Description" varchar(255) NULL, + "OwnerUUID" uuid NULL, + "IsGroupOwned" boolean NULL, + "Area" int NULL, + "AuctionID" int NULL, + "Category" int NULL, + "ClaimDate" int NULL, + "ClaimPrice" int NULL, + "GroupUUID" uuid NULL, + "SalePrice" int NULL, + "LandStatus" int NULL, + "LandFlags" int NULL, + "LandingType" int NULL, + "MediaAutoScale" int NULL, + "MediaTextureUUID" uuid NULL, + "MediaURL" varchar(255) NULL, + "MusicURL" varchar(255) NULL, + "PassHours" double precision NULL, + "PassPrice" int NULL, + "SnapshotUUID" uuid NULL, + "UserLocationX" double precision NULL, + "UserLocationY" double precision NULL, + "UserLocationZ" double precision NULL, + "UserLookAtX" double precision NULL, + "UserLookAtY" double precision NULL, + "UserLookAtZ" double precision NULL, + "AuthbuyerID" uuid NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + "OtherCleanTime" int NOT NULL DEFAULT ((0)), + "Dwell" int NOT NULL DEFAULT ((0)) + ); + +INSERT INTO Tmp_land ("UUID", "RegionUUID", "LocalLandID", "Bitmap", "Name", "Description", "OwnerUUID", "IsGroupOwned", "Area", "AuctionID", "Category", "ClaimDate", "ClaimPrice", "GroupUUID", "SalePrice", "LandStatus", "LandFlags", "LandingType", "MediaAutoScale", "MediaTextureUUID", "MediaURL", "MusicURL", "PassHours", "PassPrice", "SnapshotUUID", "UserLocationX", "UserLocationY", "UserLocationZ", "UserLookAtX", "UserLookAtY", "UserLookAtZ", "AuthbuyerID", "OtherCleanTime", "Dwell") + SELECT cast("UUID" as uuid), cast("RegionUUID" as uuid), "LocalLandID", "Bitmap", "Name", "Description", cast("OwnerUUID" as uuid), "IsGroupOwned", "Area", "AuctionID", "Category", "ClaimDate", "ClaimPrice", cast("GroupUUID" as uuid), "SalePrice", "LandStatus", "LandFlags", "LandingType", "MediaAutoScale", cast("MediaTextureUUID" as uuid), "MediaURL", "MusicURL", "PassHours", "PassPrice", cast("SnapshotUUID" as uuid), "UserLocationX", "UserLocationY", "UserLocationZ", "UserLookAtX", "UserLookAtY", "UserLookAtZ", cast("AuthbuyerID" as uuid), "OtherCleanTime", "Dwell" + FROM land ; + +DROP TABLE land; + +alter table Tmp_land rename to land; + +ALTER TABLE land ADD CONSTRAINT + PK__land__65A475E71BFD2C07 PRIMARY KEY + ( + "UUID" + ); + +Create index on land (lower("Name")); + +COMMIT; + + + +:VERSION 18 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_landaccesslist + ( + "LandUUID" uuid NULL, + "AccessUUID" uuid NULL, + "Flags" int NULL + ); + +INSERT INTO Tmp_landaccesslist ("LandUUID", "AccessUUID", "Flags") + SELECT cast("LandUUID" as uuid), cast("AccessUUID" as uuid), "Flags" + FROM landaccesslist ; + +DROP TABLE landaccesslist; + +alter table Tmp_landaccesslist rename to landaccesslist; + +COMMIT; + + + +:VERSION 19 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_regionban + ( + "regionUUID" uuid NOT NULL, + "bannedUUID" uuid NOT NULL, + "bannedIp" varchar(16) NOT NULL, + "bannedIpHostMask" varchar(16) NOT NULL + ); + +INSERT INTO Tmp_regionban ("regionUUID", "bannedUUID", "bannedIp", "bannedIpHostMask") + SELECT cast("regionUUID" as uuid), cast("bannedUUID" as uuid), "bannedIp", "bannedIpHostMask" + FROM regionban ; + +DROP TABLE regionban; + +alter table Tmp_regionban rename to regionban; + +COMMIT; + + +:VERSION 20 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_regionsettings + ( + "regionUUID" uuid NOT NULL, + "block_terraform" boolean NOT NULL, + "block_fly" boolean NOT NULL, + "allow_damage" boolean NOT NULL, + "restrict_pushing" boolean NOT NULL, + "allow_land_resell" boolean NOT NULL, + "allow_land_join_divide" boolean NOT NULL, + "block_show_in_search" boolean NOT NULL, + "agent_limit" int NOT NULL, + "object_bonus" double precision NOT NULL, + "maturity" int NOT NULL, + "disable_scripts" boolean NOT NULL, + "disable_collisions" boolean NOT NULL, + "disable_physics" boolean NOT NULL, + "terrain_texture_1" uuid NOT NULL, + "terrain_texture_2" uuid NOT NULL, + "terrain_texture_3" uuid NOT NULL, + "terrain_texture_4" uuid NOT NULL, + "elevation_1_nw" double precision NOT NULL, + "elevation_2_nw" double precision NOT NULL, + "elevation_1_ne" double precision NOT NULL, + "elevation_2_ne" double precision NOT NULL, + "elevation_1_se" double precision NOT NULL, + "elevation_2_se" double precision NOT NULL, + "elevation_1_sw" double precision NOT NULL, + "elevation_2_sw" double precision NOT NULL, + "water_height" double precision NOT NULL, + "terrain_raise_limit" double precision NOT NULL, + "terrain_lower_limit" double precision NOT NULL, + "use_estate_sun" boolean NOT NULL, + "fixed_sun" boolean NOT NULL, + "sun_position" double precision NOT NULL, + "covenant" uuid NULL DEFAULT (NULL), + "Sandbox" boolean NOT NULL, + "sunvectorx" double precision NOT NULL DEFAULT ((0)), + "sunvectory" double precision NOT NULL DEFAULT ((0)), + "sunvectorz" double precision NOT NULL DEFAULT ((0)) + ); + +INSERT INTO Tmp_regionsettings ("regionUUID", "block_terraform", "block_fly", "allow_damage", "restrict_pushing", "allow_land_resell", "allow_land_join_divide", "block_show_in_search", "agent_limit", "object_bonus", "maturity", "disable_scripts", "disable_collisions", "disable_physics", "terrain_texture_1", "terrain_texture_2", "terrain_texture_3", "terrain_texture_4", "elevation_1_nw", "elevation_2_nw", "elevation_1_ne", "elevation_2_ne", "elevation_1_se", "elevation_2_se", "elevation_1_sw", "elevation_2_sw", "water_height", "terrain_raise_limit", "terrain_lower_limit", "use_estate_sun", "fixed_sun", "sun_position", "covenant", "Sandbox", "sunvectorx", "sunvectory", "sunvectorz") + SELECT cast("regionUUID" as uuid), "block_terraform", "block_fly", "allow_damage", "restrict_pushing", "allow_land_resell", "allow_land_join_divide", "block_show_in_search", "agent_limit", "object_bonus", "maturity", "disable_scripts", "disable_collisions", "disable_physics", cast("terrain_texture_1" as uuid), cast("terrain_texture_2" as uuid), cast("terrain_texture_3" as uuid), cast("terrain_texture_4" as uuid), "elevation_1_nw", "elevation_2_nw", "elevation_1_ne", "elevation_2_ne", "elevation_1_se", "elevation_2_se", "elevation_1_sw", "elevation_2_sw", "water_height", "terrain_raise_limit", "terrain_lower_limit", "use_estate_sun", "fixed_sun", "sun_position", cast("covenant" as uuid), "Sandbox", "sunvectorx", "sunvectory", "sunvectorz" + FROM regionsettings ; + +DROP TABLE regionsettings; + +alter table Tmp_regionsettings rename to regionsettings; + +ALTER TABLE regionsettings ADD CONSTRAINT + PK__regionse__5B35159D21B6055D PRIMARY KEY + ( + "regionUUID" + ); + +COMMIT; + + +:VERSION 21 + +BEGIN TRANSACTION; + +ALTER TABLE prims ADD "PassTouches" boolean not null default false; + +COMMIT; + + +:VERSION 22 + +BEGIN TRANSACTION; + +ALTER TABLE regionsettings ADD "loaded_creation_date" varchar(20) ; +ALTER TABLE regionsettings ADD "loaded_creation_time" varchar(20) ; +ALTER TABLE regionsettings ADD "loaded_creation_id" varchar(64) ; + +COMMIT; + +:VERSION 23 + +BEGIN TRANSACTION; + +ALTER TABLE regionsettings DROP COLUMN "loaded_creation_date"; +ALTER TABLE regionsettings DROP COLUMN "loaded_creation_time"; +ALTER TABLE regionsettings ADD "loaded_creation_datetime" int NOT NULL default 0; + +COMMIT; + +:VERSION 24 + +BEGIN TRANSACTION; + +ALTER TABLE prims ADD "MediaURL" varchar(255); +ALTER TABLE primshapes ADD "Media" TEXT NULL; + +COMMIT; + +:VERSION 25 + +BEGIN TRANSACTION; +CREATE TABLE regionwindlight ( + "region_id" varchar(36) NOT NULL DEFAULT '000000-0000-0000-0000-000000000000' PRIMARY KEY, + "water_color_r" double precision NOT NULL DEFAULT '4.000000', + water_color_g double precision NOT NULL DEFAULT '38.000000', + water_color_b double precision NOT NULL DEFAULT '64.000000', + water_fog_density_exponent double precision NOT NULL DEFAULT '4.0', + underwater_fog_modifier double precision NOT NULL DEFAULT '0.25', + reflection_wavelet_scale_1 double precision NOT NULL DEFAULT '2.0', + reflection_wavelet_scale_2 double precision NOT NULL DEFAULT '2.0', + reflection_wavelet_scale_3 double precision NOT NULL DEFAULT '2.0', + fresnel_scale double precision NOT NULL DEFAULT '0.40', + fresnel_offset double precision NOT NULL DEFAULT '0.50', + refract_scale_above double precision NOT NULL DEFAULT '0.03', + refract_scale_below double precision NOT NULL DEFAULT '0.20', + blur_multiplier double precision NOT NULL DEFAULT '0.040', + big_wave_direction_x double precision NOT NULL DEFAULT '1.05', + big_wave_direction_y double precision NOT NULL DEFAULT '-0.42', + little_wave_direction_x double precision NOT NULL DEFAULT '1.11', + little_wave_direction_y double precision NOT NULL DEFAULT '-1.16', + normal_map_texture varchar(36) NOT NULL DEFAULT '822ded49-9a6c-f61c-cb89-6df54f42cdf4', + horizon_r double precision NOT NULL DEFAULT '0.25', + horizon_g double precision NOT NULL DEFAULT '0.25', + horizon_b double precision NOT NULL DEFAULT '0.32', + horizon_i double precision NOT NULL DEFAULT '0.32', + haze_horizon double precision NOT NULL DEFAULT '0.19', + blue_density_r double precision NOT NULL DEFAULT '0.12', + blue_density_g double precision NOT NULL DEFAULT '0.22', + blue_density_b double precision NOT NULL DEFAULT '0.38', + blue_density_i double precision NOT NULL DEFAULT '0.38', + haze_density double precision NOT NULL DEFAULT '0.70', + density_multiplier double precision NOT NULL DEFAULT '0.18', + distance_multiplier double precision NOT NULL DEFAULT '0.8', + max_altitude int NOT NULL DEFAULT '1605', + sun_moon_color_r double precision NOT NULL DEFAULT '0.24', + sun_moon_color_g double precision NOT NULL DEFAULT '0.26', + sun_moon_color_b double precision NOT NULL DEFAULT '0.30', + sun_moon_color_i double precision NOT NULL DEFAULT '0.30', + sun_moon_position double precision NOT NULL DEFAULT '0.317', + ambient_r double precision NOT NULL DEFAULT '0.35', + ambient_g double precision NOT NULL DEFAULT '0.35', + ambient_b double precision NOT NULL DEFAULT '0.35', + ambient_i double precision NOT NULL DEFAULT '0.35', + east_angle double precision NOT NULL DEFAULT '0.00', + sun_glow_focus double precision NOT NULL DEFAULT '0.10', + sun_glow_size double precision NOT NULL DEFAULT '1.75', + scene_gamma double precision NOT NULL DEFAULT '1.00', + star_brightness double precision NOT NULL DEFAULT '0.00', + cloud_color_r double precision NOT NULL DEFAULT '0.41', + cloud_color_g double precision NOT NULL DEFAULT '0.41', + cloud_color_b double precision NOT NULL DEFAULT '0.41', + cloud_color_i double precision NOT NULL DEFAULT '0.41', + cloud_x double precision NOT NULL DEFAULT '1.00', + cloud_y double precision NOT NULL DEFAULT '0.53', + cloud_density double precision NOT NULL DEFAULT '1.00', + cloud_coverage double precision NOT NULL DEFAULT '0.27', + cloud_scale double precision NOT NULL DEFAULT '0.42', + cloud_detail_x double precision NOT NULL DEFAULT '1.00', + cloud_detail_y double precision NOT NULL DEFAULT '0.53', + cloud_detail_density double precision NOT NULL DEFAULT '0.12', + cloud_scroll_x double precision NOT NULL DEFAULT '0.20', + cloud_scroll_x_lock smallint NOT NULL DEFAULT '0', + cloud_scroll_y double precision NOT NULL DEFAULT '0.01', + cloud_scroll_y_lock smallint NOT NULL DEFAULT '0', + draw_classic_clouds smallint NOT NULL DEFAULT '1' +); + +COMMIT; + +:VERSION 26 + +BEGIN TRANSACTION; + +ALTER TABLE regionsettings ADD "map_tile_ID" CHAR(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; + +COMMIT; + +:VERSION 27 #--------------------- + +BEGIN TRANSACTION; +ALTER TABLE land ADD "MediaType" VARCHAR(32) NOT NULL DEFAULT 'none/none' ; +ALTER TABLE land ADD "MediaDescription" VARCHAR(255) NOT NULL DEFAULT ''; +ALTER TABLE land ADD "MediaSize" VARCHAR(16) NOT NULL DEFAULT '0,0'; +ALTER TABLE land ADD "MediaLoop" boolean NOT NULL DEFAULT false; +ALTER TABLE land ADD "ObscureMusic" boolean NOT NULL DEFAULT false; +ALTER TABLE land ADD "ObscureMedia" boolean NOT NULL DEFAULT false; +COMMIT; + +:VERSION 28 #--------------------- + +BEGIN TRANSACTION; + +ALTER TABLE prims +alter column "CreatorID" set DEFAULT '00000000-0000-0000-0000-000000000000' ; + +ALTER TABLE prims ALTER COLUMN "CreatorID" set NOT NULL; + +ALTER TABLE primitems +alter column "creatorID" set DEFAULT '00000000-0000-0000-0000-000000000000' ; + +ALTER TABLE primitems ALTER COLUMN "creatorID" set NOT NULL; + +COMMIT; + +:VERSION 29 #----------------- Region Covenant changed time + +BEGIN TRANSACTION; + +ALTER TABLE regionsettings ADD "covenant_datetime" int NOT NULL default 0; + +COMMIT; + +:VERSION 30 #------------------Migrate "creatorID" storage to varchars instead of UUIDs for HG support + +BEGIN TRANSACTION; + +alter table prims rename column "CreatorID" to "CreatorIDOld"; +alter table primitems rename column "creatorID" to "creatorIDOld"; + +COMMIT; + +:VERSION 31 #--------------------- + +BEGIN TRANSACTION; + +ALTER TABLE prims ADD "CreatorID" varchar(255); +ALTER TABLE primitems ADD "creatorID" varchar(255); + +COMMIT; + +:VERSION 32 #--------------------- + +BEGIN TRANSACTION; + +UPDATE prims SET "CreatorID" = cast("CreatorIDOld" as varchar(255)); +UPDATE primitems SET "creatorID" = cast("creatorIDOld" as varchar(255)); + +COMMIT; + +:VERSION 33 #--------------------- + +BEGIN TRANSACTION; + +ALTER TABLE prims alter column "CreatorID" set default '00000000-0000-0000-0000-000000000000' ; + +ALTER TABLE prims ALTER COLUMN "CreatorID" set NOT NULL; + +ALTER TABLE primitems alter column "creatorID" set DEFAULT '00000000-0000-0000-0000-000000000000' ; + +ALTER TABLE primitems ALTER COLUMN "creatorID" set NOT NULL; + +COMMIT; + +:VERSION 34 #--------------- Telehub support + +BEGIN TRANSACTION; + +CREATE TABLE spawn_points( + "RegionUUID" uuid NOT NULL PRIMARY KEY, + "Yaw" double precision NOT NULL, + "Pitch" double precision NOT NULL, + "Distance" double precision NOT NULL +); + +ALTER TABLE regionsettings ADD "TelehubObject" uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; + +COMMIT; + +:VERSION 35 #---------------- Parcels for sale + +BEGIN TRANSACTION; + +ALTER TABLE regionsettings ADD "parcel_tile_ID" uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; + +COMMIT; + +:VERSION 36 #---------------- Timed bans/access + +BEGIN TRANSACTION; + +ALTER TABLE landaccesslist ADD "Expires" integer NOT NULL DEFAULT 0; + +COMMIT; + +:VERSION 37 #---------------- Environment Settings + +BEGIN TRANSACTION; + +CREATE TABLE regionenvironment( + "region_id" uuid NOT NULL primary key, + "llsd_settings" varchar NOT NULL +); + +COMMIT; + +:VERSION 38 #---------------- Dynamic attributes + +BEGIN TRANSACTION; + +ALTER TABLE prims ADD "DynAttrs" TEXT; + +COMMIT; + +:VERSION 39 #---------------- Extra physics params + +BEGIN TRANSACTION; + +ALTER TABLE prims ADD "PhysicsShapeType" smallint NOT NULL default '0'; +ALTER TABLE prims ADD "Density" double precision NOT NULL default '1000'; +ALTER TABLE prims ADD "GravityModifier" double precision NOT NULL default '1'; +ALTER TABLE prims ADD "Friction" double precision NOT NULL default '0.6'; +ALTER TABLE prims ADD "Restitution" double precision NOT NULL default '0.5'; + +COMMIT; + +:VERSION 40 #-- regionwindlight changed type from smallint to bool + +BEGIN TRANSACTION; + +ALTER TABLE regionwindlight ALTER COLUMN cloud_scroll_x_lock DROP DEFAULT; +ALTER TABLE regionwindlight ALTER cloud_scroll_x_lock TYPE bool USING CASE WHEN cloud_scroll_x_lock=0 THEN FALSE ELSE TRUE END; +ALTER TABLE regionwindlight ALTER COLUMN cloud_scroll_x_lock SET DEFAULT FALSE; + +ALTER TABLE regionwindlight ALTER COLUMN cloud_scroll_y_lock DROP DEFAULT; +ALTER TABLE regionwindlight ALTER cloud_scroll_y_lock TYPE bool USING CASE WHEN cloud_scroll_y_lock=0 THEN FALSE ELSE TRUE END; +ALTER TABLE regionwindlight ALTER COLUMN cloud_scroll_y_lock SET DEFAULT FALSE; + +ALTER TABLE regionwindlight ALTER COLUMN draw_classic_clouds DROP DEFAULT; +ALTER TABLE regionwindlight ALTER draw_classic_clouds TYPE bool USING CASE WHEN draw_classic_clouds=0 THEN FALSE ELSE TRUE END; +ALTER TABLE regionwindlight ALTER COLUMN draw_classic_clouds SET DEFAULT FALSE; + +COMMIT; diff --git a/OpenSim/Data/PGSQL/Resources/UserAccount.migrations b/OpenSim/Data/PGSQL/Resources/UserAccount.migrations new file mode 100644 index 0000000000..c785463325 --- /dev/null +++ b/OpenSim/Data/PGSQL/Resources/UserAccount.migrations @@ -0,0 +1,51 @@ +:VERSION 1 + +CREATE TABLE UserAccounts ( + "PrincipalID" uuid NOT NULL Primary key, + "ScopeID" uuid NOT NULL, + "FirstName" varchar(64) NOT NULL, + "LastName" varchar(64) NOT NULL, + "Email" varchar(64) NULL, + "ServiceURLs" text NULL, + "Created" int default NULL +); + + +:VERSION 2 + +BEGIN TRANSACTION; + +INSERT INTO UserAccounts ("PrincipalID", "ScopeID", "FirstName", "LastName", "Email", "ServiceURLs", "Created") +SELECT UUID AS "PrincipalID", '00000000-0000-0000-0000-000000000000' AS "ScopeID", +username AS "FirstName", +lastname AS "LastName", +email as "Email", ( +'AssetServerURI=' + +userAssetURI + ' InventoryServerURI=' + userInventoryURI + ' GatewayURI= HomeURI=') AS "ServiceURLs", +created as "Created" FROM users; + +COMMIT; + +:VERSION 3 + +BEGIN TRANSACTION; + +CREATE UNIQUE INDEX "PrincipalID" ON UserAccounts("PrincipalID"); +CREATE INDEX "Email" ON UserAccounts("Email"); +CREATE INDEX "FirstName" ON UserAccounts("FirstName"); +CREATE INDEX "LastName" ON UserAccounts("LastName"); +CREATE INDEX Name ON UserAccounts("FirstName","LastName"); + +COMMIT; + +:VERSION 4 + +BEGIN TRANSACTION; + +ALTER TABLE UserAccounts ADD "UserLevel" integer NOT NULL DEFAULT 0; +ALTER TABLE UserAccounts ADD "UserFlags" integer NOT NULL DEFAULT 0; +ALTER TABLE UserAccounts ADD "UserTitle" varchar(64) NOT NULL DEFAULT ''; + +COMMIT; + + diff --git a/OpenSim/Data/PGSQL/Resources/UserProfiles.migrations b/OpenSim/Data/PGSQL/Resources/UserProfiles.migrations new file mode 100644 index 0000000000..f23c87087c --- /dev/null +++ b/OpenSim/Data/PGSQL/Resources/UserProfiles.migrations @@ -0,0 +1,83 @@ +:VERSION 1 # ------------------------------- + +begin; + +CREATE TABLE classifieds ( + "classifieduuid" char(36) NOT NULL, + "creatoruuid" char(36) NOT NULL, + "creationdate" integer NOT NULL, + "expirationdate" integer NOT NULL, + "category" varchar(20) NOT NULL, + "name" varchar(255) NOT NULL, + "description" text NOT NULL, + "parceluuid" char(36) NOT NULL, + "parentestate" integer NOT NULL, + "snapshotuuid" char(36) NOT NULL, + "simname" varchar(255) NOT NULL, + "posglobal" varchar(255) NOT NULL, + "parcelname" varchar(255) NOT NULL, + "classifiedflags" integer NOT NULL, + "priceforlisting" integer NOT NULL, + constraint classifiedspk PRIMARY KEY ("classifieduuid") +); + + +CREATE TABLE usernotes ( + "useruuid" varchar(36) NOT NULL, + "targetuuid" varchar(36) NOT NULL, + "notes" text NOT NULL, + constraint usernoteuk UNIQUE ("useruuid","targetuuid") +); + + +CREATE TABLE userpicks ( + "pickuuid" varchar(36) NOT NULL, + "creatoruuid" varchar(36) NOT NULL, + "toppick" boolean NOT NULL, + "parceluuid" varchar(36) NOT NULL, + "name" varchar(255) NOT NULL, + "description" text NOT NULL, + "snapshotuuid" varchar(36) NOT NULL, + "user" varchar(255) NOT NULL, + "originalname" varchar(255) NOT NULL, + "simname" varchar(255) NOT NULL, + "posglobal" varchar(255) NOT NULL, + "sortorder" integer NOT NULL, + "enabled" boolean NOT NULL, + PRIMARY KEY ("pickuuid") +); + + +CREATE TABLE userprofile ( + "useruuid" varchar(36) NOT NULL, + "profilePartner" varchar(36) NOT NULL, + "profileAllowPublish" bytea NOT NULL, + "profileMaturePublish" bytea NOT NULL, + "profileURL" varchar(255) NOT NULL, + "profileWantToMask" integer NOT NULL, + "profileWantToText" text NOT NULL, + "profileSkillsMask" integer NOT NULL, + "profileSkillsText" text NOT NULL, + "profileLanguages" text NOT NULL, + "profileImage" varchar(36) NOT NULL, + "profileAboutText" text NOT NULL, + "profileFirstImage" varchar(36) NOT NULL, + "profileFirstText" text NOT NULL, + PRIMARY KEY ("useruuid") +); + +commit; + +:VERSION 2 # ------------------------------- + +begin; +CREATE TABLE userdata ( + "UserId" char(36) NOT NULL, + "TagId" varchar(64) NOT NULL, + "DataKey" varchar(255), + "DataVal" varchar(255), + PRIMARY KEY ("UserId","TagId") +); + +commit; + diff --git a/OpenSim/Data/PGSQL/Resources/UserStore.migrations b/OpenSim/Data/PGSQL/Resources/UserStore.migrations new file mode 100644 index 0000000000..974d48995b --- /dev/null +++ b/OpenSim/Data/PGSQL/Resources/UserStore.migrations @@ -0,0 +1,404 @@ +:VERSION 1 + +CREATE TABLE users ( + "UUID" varchar(36) NOT NULL default '' Primary Key, + "username" varchar(32) NOT NULL, + "lastname" varchar(32) NOT NULL, + "passwordHash" varchar(32) NOT NULL, + "passwordSalt" varchar(32) NOT NULL, + "homeRegion" bigint default NULL, + "homeLocationX" double precision default NULL, + "homeLocationY" double precision default NULL, + "homeLocationZ" double precision default NULL, + "homeLookAtX" double precision default NULL, + "homeLookAtY" double precision default NULL, + "homeLookAtZ" double precision default NULL, + "created" int NOT NULL, + "lastLogin" int NOT NULL, + "userInventoryURI" varchar(255) default NULL, + "userAssetURI" varchar(255) default NULL, + "profileCanDoMask" int default NULL, + "profileWantDoMask" int default NULL, + "profileAboutText" text, + "profileFirstText" text, + "profileImage" varchar(36) default NULL, + "profileFirstImage" varchar(36) default NULL, + "webLoginKey" varchar(36) default NULL +); + +CREATE INDEX "usernames" ON users +( + "username" ASC, + "lastname" ASC +); + + +CREATE TABLE agents ( + "UUID" varchar(36) NOT NULL Primary Key, + "sessionID" varchar(36) NOT NULL, + "secureSessionID" varchar(36) NOT NULL, + "agentIP" varchar(16) NOT NULL, + "agentPort" int NOT NULL, + "agentOnline" smallint NOT NULL, + "loginTime" int NOT NULL, + "logoutTime" int NOT NULL, + "currentRegion" varchar(36) NOT NULL, + "currentHandle" bigint NOT NULL, + "currentPos" varchar(64) NOT NULL +); + +CREATE INDEX session ON agents +( + "sessionID" ASC +); + +CREATE INDEX ssession ON agents +( + "secureSessionID" ASC +); + + +CREATE TABLE userfriends( + "ownerID" varchar(50) NOT NULL, + "friendID" varchar(50) NOT NULL, + "friendPerms" varchar(50) NOT NULL, + "datetimestamp" varchar(50) NOT NULL +); + +CREATE TABLE avatarappearance ( + "Owner" varchar(36) NOT NULL primary key, + "Serial" int NOT NULL, + "Visual_Params" bytea NOT NULL, + "Texture" bytea NOT NULL, + "Avatar_Height" double precision NOT NULL, + "Body_Item" varchar(36) NOT NULL, + "Body_Asset" varchar(36) NOT NULL, + "Skin_Item" varchar(36) NOT NULL, + "Skin_Asset" varchar(36) NOT NULL, + "Hair_Item" varchar(36) NOT NULL, + "Hair_Asset" varchar(36) NOT NULL, + "Eyes_Item" varchar(36) NOT NULL, + "Eyes_Asset" varchar(36) NOT NULL, + "Shirt_Item" varchar(36) NOT NULL, + "Shirt_Asset" varchar(36) NOT NULL, + "Pants_Item" varchar(36) NOT NULL, + "Pants_Asset" varchar(36) NOT NULL, + "Shoes_Item" varchar(36) NOT NULL, + "Shoes_Asset" varchar(36) NOT NULL, + "Socks_Item" varchar(36) NOT NULL, + "Socks_Asset" varchar(36) NOT NULL, + "Jacket_Item" varchar(36) NOT NULL, + "Jacket_Asset" varchar(36) NOT NULL, + "Gloves_Item" varchar(36) NOT NULL, + "Gloves_Asset" varchar(36) NOT NULL, + "Undershirt_Item" varchar(36) NOT NULL, + "Undershirt_Asset" varchar(36) NOT NULL, + "Underpants_Item" varchar(36) NOT NULL, + "Underpants_Asset" varchar(36) NOT NULL, + "Skirt_Item" varchar(36) NOT NULL, + "Skirt_Asset" varchar(36) NOT NULL +); + +:VERSION 2 + +BEGIN TRANSACTION; + +ALTER TABLE users ADD "homeRegionID" varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000'; +ALTER TABLE users ADD "userFlags" int NOT NULL default 0; +ALTER TABLE users ADD "godLevel" int NOT NULL default 0; +ALTER TABLE users ADD "customType" varchar(32) not null default ''; +ALTER TABLE users ADD "partner" varchar(36) not null default '00000000-0000-0000-0000-000000000000'; + +COMMIT; + + +:VERSION 3 + +BEGIN TRANSACTION; + +CREATE TABLE avatarattachments ( + "UUID" varchar(36) NOT NULL + , "attachpoint" int NOT NULL + , item varchar(36) NOT NULL + , asset varchar(36) NOT NULL); + +CREATE INDEX IX_avatarattachments ON avatarattachments + ( + "UUID" + ); + +COMMIT; + + +:VERSION 4 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_userfriends + ( + "ownerID" varchar(36) NOT NULL, + "friendID" varchar(36) NOT NULL, + "friendPerms" int NOT NULL, + "datetimestamp" int NOT NULL + ); + +INSERT INTO Tmp_userfriends ("ownerID", "friendID", "friendPerms", "datetimestamp") + SELECT cast("ownerID" as varchar(36)), cast("friendID" as varchar(36)), cast("friendPerms" as int), cast("datetimestamp" as int) + FROM userfriends; + +DROP TABLE userfriends; + +alter table Tmp_userfriends rename to userfriends; + +CREATE INDEX IX_userfriends_ownerID ON userfriends + ( + "ownerID" + ); + +CREATE INDEX IX_userfriends_friendID ON userfriends + ( + "friendID" + ); + +COMMIT; + + +:VERSION 5 + +BEGIN TRANSACTION; + + ALTER TABLE users add "email" varchar(250); + +COMMIT; + + +:VERSION 6 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_users + ( + "UUID" uuid NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + "username" varchar(32) NOT NULL, + "lastname" varchar(32) NOT NULL, + "passwordHash" varchar(32) NOT NULL, + "passwordSalt" varchar(32) NOT NULL, + "homeRegion" bigint NULL DEFAULT (NULL), + "homeLocationX" double precision NULL DEFAULT (NULL), + "homeLocationY" double precision NULL DEFAULT (NULL), + "homeLocationZ" double precision NULL DEFAULT (NULL), + "homeLookAtX" double precision NULL DEFAULT (NULL), + "homeLookAtY" double precision NULL DEFAULT (NULL), + "homeLookAtZ" double precision NULL DEFAULT (NULL), + "created" int NOT NULL, + "lastLogin" int NOT NULL, + "userInventoryURI" varchar(255) NULL DEFAULT (NULL), + "userAssetURI" varchar(255) NULL DEFAULT (NULL), + "profileCanDoMask" int NULL DEFAULT (NULL), + "profileWantDoMask" int NULL DEFAULT (NULL), + "profileAboutText" text NULL, + "profileFirstText" text NULL, + "profileImage" uuid NULL DEFAULT (NULL), + "profileFirstImage" uuid NULL DEFAULT (NULL), + "webLoginKey" uuid NULL DEFAULT (NULL), + "homeRegionID" uuid NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + "userFlags" int NOT NULL DEFAULT ((0)), + "godLevel" int NOT NULL DEFAULT ((0)), + "customType" varchar(32) NOT NULL DEFAULT (''), + "partner" uuid NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + email varchar(250) NULL + ); + +INSERT INTO Tmp_users ("UUID", "username", "lastname", "passwordHash", "passwordSalt", "homeRegion", "homeLocationX", "homeLocationY", "homeLocationZ", "homeLookAtX", "homeLookAtY", "homeLookAtZ", "created", "lastLogin", "userInventoryURI", "userAssetURI", "profileCanDoMask", "profileWantDoMask", "profileAboutText", "profileFirstText", "profileImage", "profileFirstImage", "webLoginKey", "homeRegionID", "userFlags", "godLevel", "customType", "partner", email) + SELECT cast("UUID" as uuid), "username", "lastname", "passwordHash", "passwordSalt", "homeRegion", "homeLocationX", "homeLocationY", "homeLocationZ", "homeLookAtX", "homeLookAtY", "homeLookAtZ", "created", "lastLogin", "userInventoryURI", "userAssetURI", "profileCanDoMask", "profileWantDoMask", "profileAboutText", "profileFirstText", cast("profileImage" as uuid), cast("profileFirstImage" as uuid), cast("webLoginKey" as uuid), cast("homeRegionID" as uuid), "userFlags", "godLevel", "customType", cast("partner" as uuid), email + FROM users ; + +DROP TABLE users; + +alter table Tmp_users rename to users; + +ALTER TABLE users ADD CONSTRAINT + PK__users__65A475E737A5467C PRIMARY KEY + ( + "UUID" + ); + +CREATE INDEX "usernames" ON users + ( + "username", + "lastname" + ); + +COMMIT; + + +:VERSION 7 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_agents + ( + "UUID" uuid NOT NULL, + "sessionID" uuid NOT NULL, + "secureSessionID" uuid NOT NULL, + "agentIP" varchar(16) NOT NULL, + "agentPort" int NOT NULL, + "agentOnline" smallint NOT NULL, + "loginTime" int NOT NULL, + "logoutTime" int NOT NULL, + "currentRegion" uuid NOT NULL, + "currentHandle" bigint NOT NULL, + "currentPos" varchar(64) NOT NULL + ); + +INSERT INTO Tmp_agents ("UUID", "sessionID", "secureSessionID", "agentIP", "agentPort", "agentOnline", "loginTime", "logoutTime", "currentRegion", "currentHandle", "currentPos") + SELECT cast("UUID" as uuid), cast("sessionID" as uuid), cast("secureSessionID" as uuid), "agentIP", "agentPort", "agentOnline", "loginTime", "logoutTime", cast("currentRegion" as uuid), "currentHandle", "currentPos" + FROM agents ; + +DROP TABLE agents; + +alter table Tmp_agents rename to agents; + +ALTER TABLE agents ADD CONSTRAINT + PK__agents__65A475E749C3F6B7 PRIMARY KEY + ( + "UUID" + ) ; + +CREATE INDEX session ON agents + ( + "sessionID" + ); + +CREATE INDEX ssession ON agents + ( + "secureSessionID" + ); + +COMMIT; + + +:VERSION 8 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_userfriends + ( + "ownerID" uuid NOT NULL, + "friendID" uuid NOT NULL, + "friendPerms" int NOT NULL, + "datetimestamp" int NOT NULL + ); + +INSERT INTO Tmp_userfriends ("ownerID", "friendID", "friendPerms", "datetimestamp") + SELECT cast("ownerID" as uuid), cast( "friendID" as uuid), "friendPerms", "datetimestamp" + FROM userfriends; + +DROP TABLE userfriends; + +alter table Tmp_userfriends rename to userfriends; + +CREATE INDEX IX_userfriends_ownerID ON userfriends + ( + "ownerID" + ); + +CREATE INDEX IX_userfriends_friendID ON userfriends + ( + "friendID" + ); + +COMMIT; + + +:VERSION 9 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_avatarappearance + ( + "Owner" uuid NOT NULL, + "Serial" int NOT NULL, + "Visual_Params" bytea NOT NULL, + "Texture" bytea NOT NULL, + "Avatar_Height" double precision NOT NULL, + "Body_Item" uuid NOT NULL, + "Body_Asset" uuid NOT NULL, + "Skin_Item" uuid NOT NULL, + "Skin_Asset" uuid NOT NULL, + "Hair_Item" uuid NOT NULL, + "Hair_Asset" uuid NOT NULL, + "Eyes_Item" uuid NOT NULL, + "Eyes_Asset" uuid NOT NULL, + "Shirt_Item" uuid NOT NULL, + "Shirt_Asset" uuid NOT NULL, + "Pants_Item" uuid NOT NULL, + "Pants_Asset" uuid NOT NULL, + "Shoes_Item" uuid NOT NULL, + "Shoes_Asset" uuid NOT NULL, + "Socks_Item" uuid NOT NULL, + "Socks_Asset" uuid NOT NULL, + "Jacket_Item" uuid NOT NULL, + "Jacket_Asset" uuid NOT NULL, + "Gloves_Item" uuid NOT NULL, + "Gloves_Asset" uuid NOT NULL, + "Undershirt_Item" uuid NOT NULL, + "Undershirt_Asset" uuid NOT NULL, + "Underpants_Item" uuid NOT NULL, + "Underpants_Asset" uuid NOT NULL, + "Skirt_Item" uuid NOT NULL, + "Skirt_Asset" uuid NOT NULL + ); + +INSERT INTO Tmp_avatarappearance ("Owner", "Serial", "Visual_Params", "Texture", "Avatar_Height", "Body_Item", "Body_Asset", "Skin_Item", "Skin_Asset", "Hair_Item", "Hair_Asset", "Eyes_Item", "Eyes_Asset", "Shirt_Item", "Shirt_Asset", "Pants_Item", "Pants_Asset", "Shoes_Item", "Shoes_Asset", "Socks_Item", "Socks_Asset", "Jacket_Item", "Jacket_Asset", "Gloves_Item", "Gloves_Asset", "Undershirt_Item", "Undershirt_Asset", "Underpants_Item", "Underpants_Asset", "Skirt_Item", "Skirt_Asset") + SELECT cast("Owner" as uuid), "Serial", "Visual_Params", "Texture", "Avatar_Height", cast("Body_Item" as uuid), cast("Body_Asset" as uuid), cast("Skin_Item" as uuid), cast("Skin_Asset" as uuid), cast("Hair_Item" as uuid), cast("Hair_Asset" as uuid), cast("Eyes_Item" as uuid), cast("Eyes_Asset" as uuid), cast("Shirt_Item" as uuid), cast("Shirt_Asset" as uuid), cast("Pants_Item" as uuid), cast("Pants_Asset" as uuid), cast("Shoes_Item" as uuid), cast("Shoes_Asset" as uuid), cast("Socks_Item" as uuid), cast("Socks_Asset" as uuid), cast("Jacket_Item" as uuid), cast("Jacket_Asset" as uuid), cast("Gloves_Item" as uuid), cast("Gloves_Asset" as uuid), cast("Undershirt_Item" as uuid), cast("Undershirt_Asset" as uuid), cast("Underpants_Item" as uuid), cast("Underpants_Asset" as uuid), cast("Skirt_Item" as uuid), cast("Skirt_Asset" as uuid) + FROM avatarappearance ; + +DROP TABLE avatarappearance; + +alter table Tmp_avatarappearance rename to avatarappearance; + +ALTER TABLE avatarappearance ADD CONSTRAINT + PK__avatarap__7DD115CC4E88ABD4 PRIMARY KEY + ( + "Owner" + ); + +COMMIT; + + +:VERSION 10 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_avatarattachments + ( + "UUID" uuid NOT NULL, + "attachpoint" int NOT NULL, + item uuid NOT NULL, + asset uuid NOT NULL + ); + +INSERT INTO Tmp_avatarattachments ("UUID", "attachpoint", item, asset) + SELECT cast("UUID" as uuid), "attachpoint", cast(item as uuid), cast(asset as uuid) + FROM avatarattachments ; + +DROP TABLE avatarattachments; + +alter table Tmp_avatarattachments rename to avatarattachments; + +CREATE INDEX IX_avatarattachments ON avatarattachments + ( + "UUID" + ); + +COMMIT; + + +:VERSION 11 + +BEGIN TRANSACTION; + +ALTER TABLE users ADD "scopeID" uuid not null default '00000000-0000-0000-0000-000000000000'; + +COMMIT; diff --git a/OpenSim/Data/PGSQL/Resources/XAssetStore.migrations b/OpenSim/Data/PGSQL/Resources/XAssetStore.migrations new file mode 100644 index 0000000000..325ed0d520 --- /dev/null +++ b/OpenSim/Data/PGSQL/Resources/XAssetStore.migrations @@ -0,0 +1,27 @@ +# ----------------- +:VERSION 1 + +BEGIN; + +CREATE TABLE XAssetsMeta ( + "ID" char(36) NOT NULL, + "Hash" char(32) NOT NULL, + "Name" varchar(64) NOT NULL, + "Description" varchar(64) NOT NULL, + "AssetType" smallint NOT NULL, + "Local" smallint NOT NULL, + "Temporary" smallint NOT NULL, + "CreateTime" integer NOT NULL, + "AccessTime" integer NOT NULL, + "AssetFlags" integer NOT NULL, + "CreatorID" varchar(128) NOT NULL, + PRIMARY KEY ("ID") +); + +CREATE TABLE XAssetsData ( + "Hash" char(32) NOT NULL, + "Data" bytea NOT NULL, + PRIMARY KEY ("Hash") +); + +COMMIT; diff --git a/OpenSim/Data/PGSQL/Resources/os_groups_Store.migrations b/OpenSim/Data/PGSQL/Resources/os_groups_Store.migrations new file mode 100644 index 0000000000..4573f71311 --- /dev/null +++ b/OpenSim/Data/PGSQL/Resources/os_groups_Store.migrations @@ -0,0 +1,94 @@ +:VERSION 1 # -------------------------- + +BEGIN; + +CREATE TABLE os_groups_groups ( + "GroupID" char(36) Primary Key NOT NULL default '', + "Location" varchar(255) NOT NULL default '', + "Name" varchar(255) NOT NULL default '', + "Charter" text NOT NULL, + "InsigniaID" char(36) NOT NULL default '', + "FounderID" char(36) NOT NULL default '', + "MembershipFee" integer NOT NULL default '0', + "OpenEnrollment" varchar(255) NOT NULL default '', + "ShowInList" integer NOT NULL default '0', + "AllowPublish" integer NOT NULL default '0', + "MaturePublish" integer NOT NULL default '0', + "OwnerRoleID" char(36) NOT NULL default '' +); + + +CREATE TABLE os_groups_membership ( + "GroupID"char(36) NOT NULL default '', + "PrincipalID" VARCHAR(255) NOT NULL default '', + "SelectedRoleID" char(36) NOT NULL default '', + "Contribution" integer NOT NULL default '0', + "ListInProfile" integer NOT NULL default '1', + "AcceptNotices" integer NOT NULL default '1', + "AccessToken" char(36) NOT NULL default '', + constraint os_groupmemberpk primary key ("GroupID", "PrincipalID") +); + + + +CREATE TABLE os_groups_roles ( + "GroupID" char(36) NOT NULL default '', + "RoleID" char(36) NOT NULL default '', + "Name" varchar(255) NOT NULL default '', + "Description" varchar(255) NOT NULL default '', + "Title" varchar(255) NOT NULL default '', + "Powers" bigint NOT NULL default 0, + constraint os_grouprolepk PRIMARY KEY ("GroupID","RoleID") +); + + +CREATE TABLE os_groups_rolemembership ( + "GroupID" char(36) NOT NULL default '', + "RoleID" char(36) NOT NULL default '', + "PrincipalID" VARCHAR(255) NOT NULL default '', + constraint os_grouprolememberpk PRIMARY KEY ("GroupID","RoleID","PrincipalID") +); + + +CREATE TABLE os_groups_invites ( + "InviteID" char(36) NOT NULL default '', + "GroupID" char(36) NOT NULL default '', + "RoleID" char(36) NOT NULL default '', + "PrincipalID" VARCHAR(255) NOT NULL default '', + "TMStamp" timestamp NOT NULL default now(), + constraint os_groupinvitespk PRIMARY KEY ("InviteID") +); +-- UNIQUE KEY "PrincipalGroup" ("GroupID","PrincipalID") + + +CREATE TABLE os_groups_notices ( + "GroupID" char(36) NOT NULL default '', + "NoticeID" char(36) NOT NULL default '', + "TMStamp" integer NOT NULL default '0', + "FromName" varchar(255) NOT NULL default '', + "Subject" varchar(255) NOT NULL default '', + "Message" text NOT NULL, + "HasAttachment" integer NOT NULL default '0', + "AttachmentType" integer NOT NULL default '0', + "AttachmentName" varchar(128) NOT NULL default '', + "AttachmentItemID" char(36) NOT NULL default '', + "AttachmentOwnerID" varchar(255) NOT NULL default '', + constraint os_groupsnoticespk PRIMARY KEY ("NoticeID") +); +-- KEY "GroupID" ("GroupID"), +-- KEY "TMStamp" ("TMStamp") + +CREATE TABLE os_groups_principals ( + "PrincipalID" VARCHAR(255) NOT NULL default '', + "ActiveGroupID" char(36) NOT NULL default '', + constraint os_groupprincpk PRIMARY KEY ("PrincipalID") +); + +COMMIT; + +:VERSION 2 # -------------------------- + +BEGIN; + + +COMMIT; diff --git a/OpenSim/Data/Properties/AssemblyInfo.cs b/OpenSim/Data/Properties/AssemblyInfo.cs index a85f47306c..9258e55f03 100644 --- a/OpenSim/Data/Properties/AssemblyInfo.cs +++ b/OpenSim/Data/Properties/AssemblyInfo.cs @@ -61,5 +61,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: -[assembly : AssemblyVersion("0.7.6.*")] +[assembly : AssemblyVersion("0.8.0.*")] diff --git a/OpenSim/Data/SQLite/Properties/AssemblyInfo.cs b/OpenSim/Data/SQLite/Properties/AssemblyInfo.cs index 992982ce76..3873eaf65d 100644 --- a/OpenSim/Data/SQLite/Properties/AssemblyInfo.cs +++ b/OpenSim/Data/SQLite/Properties/AssemblyInfo.cs @@ -61,5 +61,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: -[assembly : AssemblyVersion("0.7.6.*")] +[assembly : AssemblyVersion("0.8.0.*")] diff --git a/OpenSim/Data/SQLite/Resources/EstateStore.migrations b/OpenSim/Data/SQLite/Resources/EstateStore.migrations index 62f6464460..0aec49b3e2 100644 --- a/OpenSim/Data/SQLite/Resources/EstateStore.migrations +++ b/OpenSim/Data/SQLite/Resources/EstateStore.migrations @@ -86,3 +86,12 @@ begin; alter table estate_settings add column DenyMinors tinyint not null default 0; commit; + +:VERSION 9 + +begin; +alter table estate_settings add column AllowLandmark tinyint not null default '1'; +alter table estate_settings add column AllowParcelChanges tinyint not null default '1'; +alter table estate_settings add column AllowSetHome tinyint not null default '1'; +commit; + diff --git a/OpenSim/Data/SQLite/Resources/HGTravelStore.migrations b/OpenSim/Data/SQLite/Resources/HGTravelStore.migrations new file mode 100644 index 0000000000..02612ce13a --- /dev/null +++ b/OpenSim/Data/SQLite/Resources/HGTravelStore.migrations @@ -0,0 +1,18 @@ +:VERSION 2 # -------------------------- + +BEGIN; + +CREATE TABLE hg_traveling_data( + SessionID VARCHAR(36) NOT NULL, + UserID VARCHAR(36) NOT NULL, + GridExternalName VARCHAR(255) NOT NULL DEFAULT "", + ServiceToken VARCHAR(255) NOT NULL DEFAULT "", + ClientIPAddress VARCHAR(16) NOT NULL DEFAULT "", + MyIPAddress VARCHAR(16) NOT NULL DEFAULT "", + TMStamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY(SessionID), + UNIQUE(UserID) +); + +COMMIT; + diff --git a/OpenSim/Data/SQLite/Resources/RegionStore.migrations b/OpenSim/Data/SQLite/Resources/RegionStore.migrations index c6f4b48e18..901068ff65 100644 --- a/OpenSim/Data/SQLite/Resources/RegionStore.migrations +++ b/OpenSim/Data/SQLite/Resources/RegionStore.migrations @@ -592,3 +592,23 @@ ALTER TABLE prims ADD COLUMN `Friction` double NOT NULL default '0.6'; ALTER TABLE prims ADD COLUMN `Restitution` double NOT NULL default '0.5'; COMMIT; + +:VERSION 29 #---------------- Keyframes + +BEGIN; + +ALTER TABLE prims ADD COLUMN `KeyframeMotion` blob; + +COMMIT; + +:VERSION 30 #---------------- Save Attachment info + +BEGIN; + +ALTER TABLE prims ADD COLUMN AttachedPosX double default '0'; +ALTER TABLE prims ADD COLUMN AttachedPosY double default '0'; +ALTER TABLE prims ADD COLUMN AttachedPosZ double default '0'; +ALTER TABLE primshapes ADD COLUMN LastAttachPoint int not null default '0'; + +COMMIT; + diff --git a/OpenSim/Data/SQLite/Resources/UserProfiles.migrations b/OpenSim/Data/SQLite/Resources/UserProfiles.migrations new file mode 100644 index 0000000000..16581f6e49 --- /dev/null +++ b/OpenSim/Data/SQLite/Resources/UserProfiles.migrations @@ -0,0 +1,90 @@ +:VERSION 1 # ------------------------------- + +begin; + +CREATE TABLE IF NOT EXISTS classifieds ( + classifieduuid char(36) NOT NULL PRIMARY KEY, + creatoruuid char(36) NOT NULL, + creationdate int(20) NOT NULL, + expirationdate int(20) NOT NULL, + category varchar(20) NOT NULL, + name varchar(255) NOT NULL, + description text NOT NULL, + parceluuid char(36) NOT NULL, + parentestate int(11) NOT NULL, + snapshotuuid char(36) NOT NULL, + simname varchar(255) NOT NULL, + posglobal varchar(255) NOT NULL, + parcelname varchar(255) NOT NULL, + classifiedflags int(8) NOT NULL, + priceforlisting int(5) NOT NULL +); + +commit; + +begin; + +CREATE TABLE IF NOT EXISTS usernotes ( + useruuid varchar(36) NOT NULL, + targetuuid varchar(36) NOT NULL, + notes text NOT NULL, + UNIQUE (useruuid,targetuuid) ON CONFLICT REPLACE +); + +commit; + +begin; + +CREATE TABLE IF NOT EXISTS userpicks ( + pickuuid varchar(36) NOT NULL PRIMARY KEY, + creatoruuid varchar(36) NOT NULL, + toppick int NOT NULL, + parceluuid varchar(36) NOT NULL, + name varchar(255) NOT NULL, + description text NOT NULL, + snapshotuuid varchar(36) NOT NULL, + user varchar(255) NOT NULL, + originalname varchar(255) NOT NULL, + simname varchar(255) NOT NULL, + posglobal varchar(255) NOT NULL, + sortorder int(2) NOT NULL, + enabled int NOT NULL +); + +commit; + +begin; + +CREATE TABLE IF NOT EXISTS userprofile ( + useruuid varchar(36) NOT NULL PRIMARY KEY, + profilePartner varchar(36) NOT NULL, + profileAllowPublish binary(1) NOT NULL, + profileMaturePublish binary(1) NOT NULL, + profileURL varchar(255) NOT NULL, + profileWantToMask int(3) NOT NULL, + profileWantToText text NOT NULL, + profileSkillsMask int(3) NOT NULL, + profileSkillsText text NOT NULL, + profileLanguages text NOT NULL, + profileImage varchar(36) NOT NULL, + profileAboutText text NOT NULL, + profileFirstImage varchar(36) NOT NULL, + profileFirstText text NOT NULL +); + +commit; + +:VERSION 2 # ------------------------------- + +begin; + +CREATE TABLE IF NOT EXISTS userdata ( + UserId char(36) NOT NULL, + TagId varchar(64) NOT NULL, + DataKey varchar(255), + DataVal varchar(255), + PRIMARY KEY (UserId,TagId) +); + +commit; + diff --git a/OpenSim/Data/SQLite/SQLiteGridUserData.cs b/OpenSim/Data/SQLite/SQLiteGridUserData.cs index 1bb5ed87d6..d8c52f8880 100644 --- a/OpenSim/Data/SQLite/SQLiteGridUserData.cs +++ b/OpenSim/Data/SQLite/SQLiteGridUserData.cs @@ -56,6 +56,10 @@ namespace OpenSim.Data.SQLite return ret[0]; } + public GridUserData[] GetAll(string userID) + { + return base.Get(String.Format("UserID LIKE '{0}%'", userID)); + } } } \ No newline at end of file diff --git a/OpenSim/Data/SQLite/SQLiteHGTravelData.cs b/OpenSim/Data/SQLite/SQLiteHGTravelData.cs new file mode 100644 index 0000000000..db288b2486 --- /dev/null +++ b/OpenSim/Data/SQLite/SQLiteHGTravelData.cs @@ -0,0 +1,82 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Data; +using System.Reflection; +using System.Threading; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; +using Mono.Data.Sqlite; + +namespace OpenSim.Data.SQLite +{ + /// + /// A SQL Interface for user grid data + /// + public class SQLiteHGTravelData : SQLiteGenericTableHandler, IHGTravelingData + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public SQLiteHGTravelData(string connectionString, string realm) + : base(connectionString, realm, "HGTravelStore") {} + + public HGTravelingData Get(UUID sessionID) + { + HGTravelingData[] ret = Get("SessionID", sessionID.ToString()); + + if (ret.Length == 0) + return null; + + return ret[0]; + } + + public HGTravelingData[] GetSessions(UUID userID) + { + return base.Get("UserID", userID.ToString()); + } + + public bool Delete(UUID sessionID) + { + return Delete("SessionID", sessionID.ToString()); + } + + public void DeleteOld() + { + using (SqliteCommand cmd = new SqliteCommand()) + { + cmd.CommandText = String.Format("delete from {0} where TMStamp < datetime('now', '-2 day') ", m_Realm); + + DoQuery(cmd); + } + + } + + } +} \ No newline at end of file diff --git a/OpenSim/Data/SQLite/SQLiteSimulationData.cs b/OpenSim/Data/SQLite/SQLiteSimulationData.cs index 76f717d379..4d6a80ad22 100644 --- a/OpenSim/Data/SQLite/SQLiteSimulationData.cs +++ b/OpenSim/Data/SQLite/SQLiteSimulationData.cs @@ -1236,6 +1236,10 @@ namespace OpenSim.Data.SQLite createCol(prims, "MediaURL", typeof(String)); + createCol(prims, "AttachedPosX", typeof(Double)); + createCol(prims, "AttachedPosY", typeof(Double)); + createCol(prims, "AttachedPosZ", typeof(Double)); + createCol(prims, "DynAttrs", typeof(String)); createCol(prims, "PhysicsShapeType", typeof(Byte)); @@ -1244,6 +1248,7 @@ namespace OpenSim.Data.SQLite createCol(prims, "Friction", typeof(Double)); createCol(prims, "Restitution", typeof(Double)); + createCol(prims, "KeyframeMotion", typeof(Byte[])); // Add in contraints prims.PrimaryKey = new DataColumn[] { prims.Columns["UUID"] }; @@ -1723,6 +1728,12 @@ namespace OpenSim.Data.SQLite prim.MediaUrl = (string)row["MediaURL"]; } + prim.AttachedPos = new Vector3( + Convert.ToSingle(row["AttachedPosX"]), + Convert.ToSingle(row["AttachedPosY"]), + Convert.ToSingle(row["AttachedPosZ"]) + ); + if (!(row["DynAttrs"] is System.DBNull)) { //m_log.DebugFormat("[SQLITE]: DynAttrs type [{0}]", row["DynAttrs"].GetType()); @@ -1739,6 +1750,20 @@ namespace OpenSim.Data.SQLite prim.Friction = Convert.ToSingle(row["Friction"]); prim.Restitution = Convert.ToSingle(row["Restitution"]); + + if (!(row["KeyframeMotion"] is DBNull)) + { + Byte[] data = (byte[])row["KeyframeMotion"]; + if (data.Length > 0) + prim.KeyframeMotion = KeyframeMotion.FromData(null, data); + else + prim.KeyframeMotion = null; + } + else + { + prim.KeyframeMotion = null; + } + return prim; } @@ -2161,7 +2186,11 @@ namespace OpenSim.Data.SQLite row["MediaURL"] = prim.MediaUrl; - if (prim.DynAttrs.Count > 0) + row["AttachedPosX"] = prim.AttachedPos.X; + row["AttachedPosY"] = prim.AttachedPos.Y; + row["AttachedPosZ"] = prim.AttachedPos.Z; + + if (prim.DynAttrs.CountNamespaces > 0) row["DynAttrs"] = prim.DynAttrs.ToXml(); else row["DynAttrs"] = null; @@ -2171,6 +2200,13 @@ namespace OpenSim.Data.SQLite row["GravityModifier"] = (double)prim.GravityModifier; row["Friction"] = (double)prim.Friction; row["Restitution"] = (double)prim.Restitution; + + if (prim.KeyframeMotion != null) + row["KeyframeMotion"] = prim.KeyframeMotion.Serialize(); + else + row["KeyframeMotion"] = new Byte[0]; + + } /// @@ -2422,6 +2458,7 @@ namespace OpenSim.Data.SQLite s.ProfileCurve = Convert.ToByte(row["ProfileCurve"]); s.ProfileHollow = Convert.ToUInt16(row["ProfileHollow"]); s.State = Convert.ToByte(row["State"]); + s.LastAttachPoint = Convert.ToByte(row["LastAttachPoint"]); byte[] textureEntry = (byte[])row["Texture"]; s.TextureEntry = textureEntry; @@ -2471,6 +2508,7 @@ namespace OpenSim.Data.SQLite row["ProfileCurve"] = s.ProfileCurve; row["ProfileHollow"] = s.ProfileHollow; row["State"] = s.State; + row["LastAttachPoint"] = s.LastAttachPoint; row["Texture"] = s.TextureEntry; row["ExtraParams"] = s.ExtraParams; diff --git a/OpenSim/Data/SQLite/SQLiteUserProfilesData.cs b/OpenSim/Data/SQLite/SQLiteUserProfilesData.cs new file mode 100644 index 0000000000..84e56b1dc0 --- /dev/null +++ b/OpenSim/Data/SQLite/SQLiteUserProfilesData.cs @@ -0,0 +1,988 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Data; +using System.Reflection; +using log4net; +#if CSharpSqlite +using Community.CsharpSqlite.Sqlite; +#else +using Mono.Data.Sqlite; +#endif +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; + +namespace OpenSim.Data.SQLite +{ + public class SQLiteUserProfilesData: IProfilesData + { + private static readonly ILog m_log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private SqliteConnection m_connection; + private string m_connectionString; + + private FieldInfo[] m_Fields; + private Dictionary m_FieldMap = + new Dictionary(); + + protected virtual Assembly Assembly + { + get { return GetType().Assembly; } + } + + public SQLiteUserProfilesData() + { + } + + public SQLiteUserProfilesData(string connectionString) + { + Initialise(connectionString); + } + + public void Initialise(string connectionString) + { + if (Util.IsWindows()) + Util.LoadArchSpecificWindowsDll("sqlite3.dll"); + + m_connectionString = connectionString; + + m_log.Info("[PROFILES_DATA]: Sqlite - connecting: "+m_connectionString); + + m_connection = new SqliteConnection(m_connectionString); + m_connection.Open(); + + Migration m = new Migration(m_connection, Assembly, "UserProfiles"); + m.Update(); + } + + private string[] FieldList + { + get { return new List(m_FieldMap.Keys).ToArray(); } + } + + #region IProfilesData implementation + public OSDArray GetClassifiedRecords(UUID creatorId) + { + OSDArray data = new OSDArray(); + string query = "SELECT classifieduuid, name FROM classifieds WHERE creatoruuid = :Id"; + IDataReader reader = null; + + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = query; + cmd.Parameters.AddWithValue(":Id", creatorId); + reader = cmd.ExecuteReader(); + } + + while (reader.Read()) + { + OSDMap n = new OSDMap(); + UUID Id = UUID.Zero; + string Name = null; + try + { + UUID.TryParse(Convert.ToString( reader["classifieduuid"]), out Id); + Name = Convert.ToString(reader["name"]); + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": UserAccount exception {0}", e.Message); + } + n.Add("classifieduuid", OSD.FromUUID(Id)); + n.Add("name", OSD.FromString(Name)); + data.Add(n); + } + + reader.Close(); + + return data; + } + public bool UpdateClassifiedRecord(UserClassifiedAdd ad, ref string result) + { + string query = string.Empty; + + query += "INSERT OR REPLACE INTO classifieds ("; + query += "`classifieduuid`,"; + query += "`creatoruuid`,"; + query += "`creationdate`,"; + query += "`expirationdate`,"; + query += "`category`,"; + query += "`name`,"; + query += "`description`,"; + query += "`parceluuid`,"; + query += "`parentestate`,"; + query += "`snapshotuuid`,"; + query += "`simname`,"; + query += "`posglobal`,"; + query += "`parcelname`,"; + query += "`classifiedflags`,"; + query += "`priceforlisting`) "; + query += "VALUES ("; + query += ":ClassifiedId,"; + query += ":CreatorId,"; + query += ":CreatedDate,"; + query += ":ExpirationDate,"; + query += ":Category,"; + query += ":Name,"; + query += ":Description,"; + query += ":ParcelId,"; + query += ":ParentEstate,"; + query += ":SnapshotId,"; + query += ":SimName,"; + query += ":GlobalPos,"; + query += ":ParcelName,"; + query += ":Flags,"; + query += ":ListingPrice ) "; + + if(string.IsNullOrEmpty(ad.ParcelName)) + ad.ParcelName = "Unknown"; + if(ad.ParcelId == null) + ad.ParcelId = UUID.Zero; + if(string.IsNullOrEmpty(ad.Description)) + ad.Description = "No Description"; + + DateTime epoch = new DateTime(1970, 1, 1); + DateTime now = DateTime.Now; + TimeSpan epochnow = now - epoch; + TimeSpan duration; + DateTime expiration; + TimeSpan epochexp; + + if(ad.Flags == 2) + { + duration = new TimeSpan(7,0,0,0); + expiration = now.Add(duration); + epochexp = expiration - epoch; + } + else + { + duration = new TimeSpan(365,0,0,0); + expiration = now.Add(duration); + epochexp = expiration - epoch; + } + ad.CreationDate = (int)epochnow.TotalSeconds; + ad.ExpirationDate = (int)epochexp.TotalSeconds; + + try { + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = query; + cmd.Parameters.AddWithValue(":ClassifiedId", ad.ClassifiedId.ToString()); + cmd.Parameters.AddWithValue(":CreatorId", ad.CreatorId.ToString()); + cmd.Parameters.AddWithValue(":CreatedDate", ad.CreationDate.ToString()); + cmd.Parameters.AddWithValue(":ExpirationDate", ad.ExpirationDate.ToString()); + cmd.Parameters.AddWithValue(":Category", ad.Category.ToString()); + cmd.Parameters.AddWithValue(":Name", ad.Name.ToString()); + cmd.Parameters.AddWithValue(":Description", ad.Description.ToString()); + cmd.Parameters.AddWithValue(":ParcelId", ad.ParcelId.ToString()); + cmd.Parameters.AddWithValue(":ParentEstate", ad.ParentEstate.ToString()); + cmd.Parameters.AddWithValue(":SnapshotId", ad.SnapshotId.ToString ()); + cmd.Parameters.AddWithValue(":SimName", ad.SimName.ToString()); + cmd.Parameters.AddWithValue(":GlobalPos", ad.GlobalPos.ToString()); + cmd.Parameters.AddWithValue(":ParcelName", ad.ParcelName.ToString()); + cmd.Parameters.AddWithValue(":Flags", ad.Flags.ToString()); + cmd.Parameters.AddWithValue(":ListingPrice", ad.Price.ToString ()); + + cmd.ExecuteNonQuery(); + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": ClassifiedesUpdate exception {0}", e.Message); + result = e.Message; + return false; + } + return true; + } + public bool DeleteClassifiedRecord(UUID recordId) + { + string query = string.Empty; + + query += "DELETE FROM classifieds WHERE "; + query += "classifieduuid = :ClasifiedId"; + + try + { + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = query; + cmd.Parameters.AddWithValue(":ClassifiedId", recordId.ToString()); + + cmd.ExecuteNonQuery(); + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": DeleteClassifiedRecord exception {0}", e.Message); + return false; + } + return true; + } + + public bool GetClassifiedInfo(ref UserClassifiedAdd ad, ref string result) + { + IDataReader reader = null; + string query = string.Empty; + + query += "SELECT * FROM classifieds WHERE "; + query += "classifieduuid = :AdId"; + + try + { + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = query; + cmd.Parameters.AddWithValue(":AdId", ad.ClassifiedId.ToString()); + + using (reader = cmd.ExecuteReader()) + { + if(reader.Read ()) + { + ad.CreatorId = new UUID(reader["creatoruuid"].ToString()); + ad.ParcelId = new UUID(reader["parceluuid"].ToString ()); + ad.SnapshotId = new UUID(reader["snapshotuuid"].ToString ()); + ad.CreationDate = Convert.ToInt32(reader["creationdate"]); + ad.ExpirationDate = Convert.ToInt32(reader["expirationdate"]); + ad.ParentEstate = Convert.ToInt32(reader["parentestate"]); + ad.Flags = (byte) Convert.ToUInt32(reader["classifiedflags"]); + ad.Category = Convert.ToInt32(reader["category"]); + ad.Price = Convert.ToInt16(reader["priceforlisting"]); + ad.Name = reader["name"].ToString(); + ad.Description = reader["description"].ToString(); + ad.SimName = reader["simname"].ToString(); + ad.GlobalPos = reader["posglobal"].ToString(); + ad.ParcelName = reader["parcelname"].ToString(); + } + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": GetPickInfo exception {0}", e.Message); + } + return true; + } + + public OSDArray GetAvatarPicks(UUID avatarId) + { + IDataReader reader = null; + string query = string.Empty; + + query += "SELECT `pickuuid`,`name` FROM userpicks WHERE "; + query += "creatoruuid = :Id"; + OSDArray data = new OSDArray(); + + try + { + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = query; + cmd.Parameters.AddWithValue(":Id", avatarId.ToString()); + + using (reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + OSDMap record = new OSDMap(); + + record.Add("pickuuid",OSD.FromString((string)reader["pickuuid"])); + record.Add("name",OSD.FromString((string)reader["name"])); + data.Add(record); + } + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": GetAvatarPicks exception {0}", e.Message); + } + return data; + } + public UserProfilePick GetPickInfo(UUID avatarId, UUID pickId) + { + IDataReader reader = null; + string query = string.Empty; + UserProfilePick pick = new UserProfilePick(); + + query += "SELECT * FROM userpicks WHERE "; + query += "creatoruuid = :CreatorId AND "; + query += "pickuuid = :PickId"; + + try + { + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = query; + cmd.Parameters.AddWithValue(":CreatorId", avatarId.ToString()); + cmd.Parameters.AddWithValue(":PickId", pickId.ToString()); + + using (reader = cmd.ExecuteReader()) + { + + while (reader.Read()) + { + string description = (string)reader["description"]; + + if (string.IsNullOrEmpty(description)) + description = "No description given."; + + UUID.TryParse((string)reader["pickuuid"], out pick.PickId); + UUID.TryParse((string)reader["creatoruuid"], out pick.CreatorId); + UUID.TryParse((string)reader["parceluuid"], out pick.ParcelId); + UUID.TryParse((string)reader["snapshotuuid"], out pick.SnapshotId); + pick.GlobalPos = (string)reader["posglobal"]; + bool.TryParse((string)reader["toppick"].ToString(), out pick.TopPick); + bool.TryParse((string)reader["enabled"].ToString(), out pick.Enabled); + pick.Name = (string)reader["name"]; + pick.Desc = description; + pick.User = (string)reader["user"]; + pick.OriginalName = (string)reader["originalname"]; + pick.SimName = (string)reader["simname"]; + pick.SortOrder = (int)reader["sortorder"]; + } + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": GetPickInfo exception {0}", e.Message); + } + return pick; + } + + public bool UpdatePicksRecord(UserProfilePick pick) + { + string query = string.Empty; + + query += "INSERT OR REPLACE INTO userpicks ("; + query += "pickuuid, "; + query += "creatoruuid, "; + query += "toppick, "; + query += "parceluuid, "; + query += "name, "; + query += "description, "; + query += "snapshotuuid, "; + query += "user, "; + query += "originalname, "; + query += "simname, "; + query += "posglobal, "; + query += "sortorder, "; + query += "enabled ) "; + query += "VALUES ("; + query += ":PickId,"; + query += ":CreatorId,"; + query += ":TopPick,"; + query += ":ParcelId,"; + query += ":Name,"; + query += ":Desc,"; + query += ":SnapshotId,"; + query += ":User,"; + query += ":Original,"; + query += ":SimName,"; + query += ":GlobalPos,"; + query += ":SortOrder,"; + query += ":Enabled) "; + + try + { + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + int top_pick; + int.TryParse(pick.TopPick.ToString(), out top_pick); + int enabled; + int.TryParse(pick.Enabled.ToString(), out enabled); + + cmd.CommandText = query; + cmd.Parameters.AddWithValue(":PickId", pick.PickId.ToString()); + cmd.Parameters.AddWithValue(":CreatorId", pick.CreatorId.ToString()); + cmd.Parameters.AddWithValue(":TopPick", top_pick); + cmd.Parameters.AddWithValue(":ParcelId", pick.ParcelId.ToString()); + cmd.Parameters.AddWithValue(":Name", pick.Name.ToString()); + cmd.Parameters.AddWithValue(":Desc", pick.Desc.ToString()); + cmd.Parameters.AddWithValue(":SnapshotId", pick.SnapshotId.ToString()); + cmd.Parameters.AddWithValue(":User", pick.User.ToString()); + cmd.Parameters.AddWithValue(":Original", pick.OriginalName.ToString()); + cmd.Parameters.AddWithValue(":SimName",pick.SimName.ToString()); + cmd.Parameters.AddWithValue(":GlobalPos", pick.GlobalPos); + cmd.Parameters.AddWithValue(":SortOrder", pick.SortOrder.ToString ()); + cmd.Parameters.AddWithValue(":Enabled", enabled); + + cmd.ExecuteNonQuery(); + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": UpdateAvatarNotes exception {0}", e.Message); + return false; + } + return true; + } + + public bool DeletePicksRecord(UUID pickId) + { + string query = string.Empty; + + query += "DELETE FROM userpicks WHERE "; + query += "pickuuid = :PickId"; + + try + { + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = query; + cmd.Parameters.AddWithValue(":PickId", pickId.ToString()); + cmd.ExecuteNonQuery(); + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": DeleteUserPickRecord exception {0}", e.Message); + return false; + } + return true; + } + + public bool GetAvatarNotes(ref UserProfileNotes notes) + { + IDataReader reader = null; + string query = string.Empty; + + query += "SELECT `notes` FROM usernotes WHERE "; + query += "useruuid = :Id AND "; + query += "targetuuid = :TargetId"; + OSDArray data = new OSDArray(); + + try + { + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = query; + cmd.Parameters.AddWithValue(":Id", notes.UserId.ToString()); + cmd.Parameters.AddWithValue(":TargetId", notes.TargetId.ToString()); + + using (reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + while (reader.Read()) + { + notes.Notes = OSD.FromString((string)reader["notes"]); + } + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": GetAvatarNotes exception {0}", e.Message); + } + return true; + } + + public bool UpdateAvatarNotes(ref UserProfileNotes note, ref string result) + { + string query = string.Empty; + bool remove; + + if(string.IsNullOrEmpty(note.Notes)) + { + remove = true; + query += "DELETE FROM usernotes WHERE "; + query += "useruuid=:UserId AND "; + query += "targetuuid=:TargetId"; + } + else + { + remove = false; + query += "INSERT OR REPLACE INTO usernotes VALUES ( "; + query += ":UserId,"; + query += ":TargetId,"; + query += ":Notes )"; + } + + try + { + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = query; + + if(!remove) + cmd.Parameters.AddWithValue(":Notes", note.Notes); + cmd.Parameters.AddWithValue(":TargetId", note.TargetId.ToString ()); + cmd.Parameters.AddWithValue(":UserId", note.UserId.ToString()); + + cmd.ExecuteNonQuery(); + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": UpdateAvatarNotes exception {0}", e.Message); + return false; + } + return true; + } + + public bool GetAvatarProperties(ref UserProfileProperties props, ref string result) + { + IDataReader reader = null; + string query = string.Empty; + + query += "SELECT * FROM userprofile WHERE "; + query += "useruuid = :Id"; + + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = query; + cmd.Parameters.AddWithValue(":Id", props.UserId.ToString()); + + + try + { + reader = cmd.ExecuteReader(); + } + catch(Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": GetAvatarProperties exception {0}", e.Message); + result = e.Message; + return false; + } + if(reader != null && reader.Read()) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": Getting data for {0}.", props.UserId); + + props.WebUrl = (string)reader["profileURL"]; + UUID.TryParse((string)reader["profileImage"], out props.ImageId); + props.AboutText = (string)reader["profileAboutText"]; + UUID.TryParse((string)reader["profileFirstImage"], out props.FirstLifeImageId); + props.FirstLifeText = (string)reader["profileFirstText"]; + UUID.TryParse((string)reader["profilePartner"], out props.PartnerId); + props.WantToMask = (int)reader["profileWantToMask"]; + props.WantToText = (string)reader["profileWantToText"]; + props.SkillsMask = (int)reader["profileSkillsMask"]; + props.SkillsText = (string)reader["profileSkillsText"]; + props.Language = (string)reader["profileLanguages"]; + } + else + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": No data for {0}", props.UserId); + + props.WebUrl = string.Empty; + props.ImageId = UUID.Zero; + props.AboutText = string.Empty; + props.FirstLifeImageId = UUID.Zero; + props.FirstLifeText = string.Empty; + props.PartnerId = UUID.Zero; + props.WantToMask = 0; + props.WantToText = string.Empty; + props.SkillsMask = 0; + props.SkillsText = string.Empty; + props.Language = string.Empty; + props.PublishProfile = false; + props.PublishMature = false; + + query = "INSERT INTO userprofile ("; + query += "useruuid, "; + query += "profilePartner, "; + query += "profileAllowPublish, "; + query += "profileMaturePublish, "; + query += "profileURL, "; + query += "profileWantToMask, "; + query += "profileWantToText, "; + query += "profileSkillsMask, "; + query += "profileSkillsText, "; + query += "profileLanguages, "; + query += "profileImage, "; + query += "profileAboutText, "; + query += "profileFirstImage, "; + query += "profileFirstText) VALUES ("; + query += ":userId, "; + query += ":profilePartner, "; + query += ":profileAllowPublish, "; + query += ":profileMaturePublish, "; + query += ":profileURL, "; + query += ":profileWantToMask, "; + query += ":profileWantToText, "; + query += ":profileSkillsMask, "; + query += ":profileSkillsText, "; + query += ":profileLanguages, "; + query += ":profileImage, "; + query += ":profileAboutText, "; + query += ":profileFirstImage, "; + query += ":profileFirstText)"; + + using (SqliteCommand put = (SqliteCommand)m_connection.CreateCommand()) + { + put.CommandText = query; + put.Parameters.AddWithValue(":userId", props.UserId.ToString()); + put.Parameters.AddWithValue(":profilePartner", props.PartnerId.ToString()); + put.Parameters.AddWithValue(":profileAllowPublish", props.PublishProfile); + put.Parameters.AddWithValue(":profileMaturePublish", props.PublishMature); + put.Parameters.AddWithValue(":profileURL", props.WebUrl); + put.Parameters.AddWithValue(":profileWantToMask", props.WantToMask); + put.Parameters.AddWithValue(":profileWantToText", props.WantToText); + put.Parameters.AddWithValue(":profileSkillsMask", props.SkillsMask); + put.Parameters.AddWithValue(":profileSkillsText", props.SkillsText); + put.Parameters.AddWithValue(":profileLanguages", props.Language); + put.Parameters.AddWithValue(":profileImage", props.ImageId.ToString()); + put.Parameters.AddWithValue(":profileAboutText", props.AboutText); + put.Parameters.AddWithValue(":profileFirstImage", props.FirstLifeImageId.ToString()); + put.Parameters.AddWithValue(":profileFirstText", props.FirstLifeText); + + put.ExecuteNonQuery(); + } + } + } + return true; + } + + public bool UpdateAvatarProperties(ref UserProfileProperties props, ref string result) + { + string query = string.Empty; + + query += "UPDATE userprofile SET "; + query += "profileURL=:profileURL, "; + query += "profileImage=:image, "; + query += "profileAboutText=:abouttext,"; + query += "profileFirstImage=:firstlifeimage,"; + query += "profileFirstText=:firstlifetext "; + query += "WHERE useruuid=:uuid"; + + try + { + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = query; + cmd.Parameters.AddWithValue(":profileURL", props.WebUrl); + cmd.Parameters.AddWithValue(":image", props.ImageId.ToString()); + cmd.Parameters.AddWithValue(":abouttext", props.AboutText); + cmd.Parameters.AddWithValue(":firstlifeimage", props.FirstLifeImageId.ToString()); + cmd.Parameters.AddWithValue(":firstlifetext", props.FirstLifeText); + cmd.Parameters.AddWithValue(":uuid", props.UserId.ToString()); + + cmd.ExecuteNonQuery(); + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": AgentPropertiesUpdate exception {0}", e.Message); + + return false; + } + return true; + } + + public bool UpdateAvatarInterests(UserProfileProperties up, ref string result) + { + string query = string.Empty; + + query += "UPDATE userprofile SET "; + query += "profileWantToMask=:WantMask, "; + query += "profileWantToText=:WantText,"; + query += "profileSkillsMask=:SkillsMask,"; + query += "profileSkillsText=:SkillsText, "; + query += "profileLanguages=:Languages "; + query += "WHERE useruuid=:uuid"; + + try + { + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = query; + cmd.Parameters.AddWithValue(":WantMask", up.WantToMask); + cmd.Parameters.AddWithValue(":WantText", up.WantToText); + cmd.Parameters.AddWithValue(":SkillsMask", up.SkillsMask); + cmd.Parameters.AddWithValue(":SkillsText", up.SkillsText); + cmd.Parameters.AddWithValue(":Languages", up.Language); + cmd.Parameters.AddWithValue(":uuid", up.UserId.ToString()); + + cmd.ExecuteNonQuery(); + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": AgentInterestsUpdate exception {0}", e.Message); + result = e.Message; + return false; + } + return true; + } + + /* + public bool UpdateUserPreferences(ref UserPreferences pref, ref string result) + { + string query = string.Empty; + + query += "UPDATE usersettings SET "; + query += "imviaemail=:ImViaEmail, "; + query += "visible=:Visible "; + query += "WHERE useruuid=:uuid"; + + try + { + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = query; + cmd.Parameters.AddWithValue(":ImViaEmail", pref.IMViaEmail); + cmd.Parameters.AddWithValue(":Visible", pref.Visible); + cmd.Parameters.AddWithValue(":uuid", pref.UserId.ToString()); + + cmd.ExecuteNonQuery(); + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": AgentInterestsUpdate exception {0}", e.Message); + result = e.Message; + return false; + } + return true; + } + + public bool GetUserPreferences(ref UserPreferences pref, ref string result) + { + IDataReader reader = null; + string query = string.Empty; + + query += "SELECT imviaemail,visible,email FROM "; + query += "usersettings WHERE "; + query += "useruuid = :Id"; + + OSDArray data = new OSDArray(); + + try + { + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = query; + cmd.Parameters.AddWithValue("?Id", pref.UserId.ToString()); + + using (reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if(reader.Read()) + { + bool.TryParse((string)reader["imviaemail"], out pref.IMViaEmail); + bool.TryParse((string)reader["visible"], out pref.Visible); + pref.EMail = (string)reader["email"]; + } + else + { + query = "INSERT INTO usersettings VALUES "; + query += "(:Id,'false','false', :Email)"; + + using (SqliteCommand put = (SqliteCommand)m_connection.CreateCommand()) + { + put.Parameters.AddWithValue(":Id", pref.UserId.ToString()); + put.Parameters.AddWithValue(":Email", pref.EMail); + put.ExecuteNonQuery(); + + } + } + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": Get preferences exception {0}", e.Message); + result = e.Message; + return false; + } + return true; + } + */ + + public bool GetUserAppData(ref UserAppData props, ref string result) + { + IDataReader reader = null; + string query = string.Empty; + + query += "SELECT * FROM `userdata` WHERE "; + query += "UserId = :Id AND "; + query += "TagId = :TagId"; + + try + { + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = query; + cmd.Parameters.AddWithValue(":Id", props.UserId.ToString()); + cmd.Parameters.AddWithValue (":TagId", props.TagId.ToString()); + + using (reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if(reader.Read()) + { + props.DataKey = (string)reader["DataKey"]; + props.DataVal = (string)reader["DataVal"]; + } + else + { + query += "INSERT INTO userdata VALUES ( "; + query += ":UserId,"; + query += ":TagId,"; + query += ":DataKey,"; + query += ":DataVal) "; + + using (SqliteCommand put = (SqliteCommand)m_connection.CreateCommand()) + { + put.Parameters.AddWithValue(":Id", props.UserId.ToString()); + put.Parameters.AddWithValue(":TagId", props.TagId.ToString()); + put.Parameters.AddWithValue(":DataKey", props.DataKey.ToString()); + put.Parameters.AddWithValue(":DataVal", props.DataVal.ToString()); + + put.ExecuteNonQuery(); + } + } + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": Requst application data exception {0}", e.Message); + result = e.Message; + return false; + } + return true; + } + public bool SetUserAppData(UserAppData props, ref string result) + { + string query = string.Empty; + + query += "UPDATE userdata SET "; + query += "TagId = :TagId, "; + query += "DataKey = :DataKey, "; + query += "DataVal = :DataVal WHERE "; + query += "UserId = :UserId AND "; + query += "TagId = :TagId"; + + try + { + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = query; + cmd.Parameters.AddWithValue(":UserId", props.UserId.ToString()); + cmd.Parameters.AddWithValue(":TagId", props.TagId.ToString ()); + cmd.Parameters.AddWithValue(":DataKey", props.DataKey.ToString ()); + cmd.Parameters.AddWithValue(":DataVal", props.DataKey.ToString ()); + + cmd.ExecuteNonQuery(); + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": SetUserData exception {0}", e.Message); + return false; + } + return true; + } + public OSDArray GetUserImageAssets(UUID avatarId) + { + IDataReader reader = null; + OSDArray data = new OSDArray(); + string query = "SELECT `snapshotuuid` FROM {0} WHERE `creatoruuid` = :Id"; + + // Get classified image assets + + + try + { + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = query; + cmd.Parameters.AddWithValue(":Id", avatarId.ToString()); + + using (reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + while(reader.Read()) + { + data.Add(new OSDString((string)reader["snapshotuuid"].ToString())); + } + } + } + + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = query; + cmd.Parameters.AddWithValue(":Id", avatarId.ToString()); + + using (reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if(reader.Read()) + { + data.Add(new OSDString((string)reader["snapshotuuid"].ToString ())); + } + } + } + + query = "SELECT `profileImage`, `profileFirstImage` FROM `userprofile` WHERE `useruuid` = :Id"; + + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = query; + cmd.Parameters.AddWithValue(":Id", avatarId.ToString()); + + using (reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if(reader.Read()) + { + data.Add(new OSDString((string)reader["profileImage"].ToString ())); + data.Add(new OSDString((string)reader["profileFirstImage"].ToString ())); + } + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[PROFILES_DATA]" + + ": GetAvatarNotes exception {0}", e.Message); + } + return data; + } + #endregion + } +} + diff --git a/OpenSim/Framework/AgentCircuitData.cs b/OpenSim/Framework/AgentCircuitData.cs index ffcc5840e8..f2fe49452a 100644 --- a/OpenSim/Framework/AgentCircuitData.cs +++ b/OpenSim/Framework/AgentCircuitData.cs @@ -128,7 +128,31 @@ namespace OpenSim.Framework /// /// Viewer's version string as reported by the viewer at login /// - public string Viewer; + private string m_viewerInternal; + + /// + /// Viewer's version string + /// + public string Viewer + { + set { m_viewerInternal = value; } + + // Try to return consistent viewer string taking into account + // that viewers have chaagned how version is reported + // See http://opensimulator.org/mantis/view.php?id=6851 + get + { + // Old style version string contains viewer name followed by a space followed by a version number + if (m_viewerInternal == null || m_viewerInternal.Contains(" ")) + { + return m_viewerInternal; + } + else // New style version contains no spaces, just version number + { + return Channel + " " + m_viewerInternal; + } + } + } /// /// The channel strinf sent by the viewer at login diff --git a/OpenSim/Framework/Animation.cs b/OpenSim/Framework/Animation.cs index 232f5a1893..34255058ca 100644 --- a/OpenSim/Framework/Animation.cs +++ b/OpenSim/Framework/Animation.cs @@ -120,5 +120,24 @@ namespace OpenSim.Framework sequenceNum = args["seq_num"].AsInteger(); } + public override bool Equals(object obj) + { + Animation other = obj as Animation; + if (other != null) + { + return (other.AnimID.Equals(this.AnimID) + && other.SequenceNum == this.SequenceNum + && other.ObjectID.Equals(this.ObjectID) ); + } + return base.Equals(obj); + } + + public override string ToString() + { + return "AnimID=" + AnimID.ToString() + + "/seq=" + SequenceNum.ToString() + + "/objID=" + ObjectID.ToString(); + } + } } diff --git a/OpenSim/Framework/AssemblyInfo.cs b/OpenSim/Framework/AssemblyInfo.cs index d6b4e6adbf..287b988c26 100644 --- a/OpenSim/Framework/AssemblyInfo.cs +++ b/OpenSim/Framework/AssemblyInfo.cs @@ -59,4 +59,4 @@ using System.Runtime.InteropServices; // Revision // -[assembly : AssemblyVersion("0.7.6.*")] +[assembly : AssemblyVersion("0.8.0.*")] diff --git a/OpenSim/Framework/AssetLoader/Filesystem/Properties/AssemblyInfo.cs b/OpenSim/Framework/AssetLoader/Filesystem/Properties/AssemblyInfo.cs index feffa26dbc..11fece32b9 100644 --- a/OpenSim/Framework/AssetLoader/Filesystem/Properties/AssemblyInfo.cs +++ b/OpenSim/Framework/AssetLoader/Filesystem/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; // Build Number // Revision // -[assembly: AssemblyVersion("0.7.6.*")] +[assembly: AssemblyVersion("0.8.0.*")] diff --git a/OpenSim/Framework/BasicDOSProtector.cs b/OpenSim/Framework/BasicDOSProtector.cs new file mode 100644 index 0000000000..89bfa94191 --- /dev/null +++ b/OpenSim/Framework/BasicDOSProtector.cs @@ -0,0 +1,275 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using System.Collections.Generic; +using System.Reflection; +using log4net; + +namespace OpenSim.Framework +{ + + public class BasicDOSProtector + { + public enum ThrottleAction + { + DoThrottledMethod, + DoThrow + } + private readonly CircularBuffer _generalRequestTimes; // General request checker + private readonly BasicDosProtectorOptions _options; + private readonly Dictionary> _deeperInspection; // per client request checker + private readonly Dictionary _tempBlocked; // blocked list + private readonly Dictionary _sessions; + private readonly System.Timers.Timer _forgetTimer; // Cleanup timer + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private readonly System.Threading.ReaderWriterLockSlim _blockLockSlim = new System.Threading.ReaderWriterLockSlim(); + private readonly System.Threading.ReaderWriterLockSlim _sessionLockSlim = new System.Threading.ReaderWriterLockSlim(); + public BasicDOSProtector(BasicDosProtectorOptions options) + { + _generalRequestTimes = new CircularBuffer(options.MaxRequestsInTimeframe + 1, true); + _generalRequestTimes.Put(0); + _options = options; + _deeperInspection = new Dictionary>(); + _tempBlocked = new Dictionary(); + _sessions = new Dictionary(); + _forgetTimer = new System.Timers.Timer(); + _forgetTimer.Elapsed += delegate + { + _forgetTimer.Enabled = false; + + List removes = new List(); + _blockLockSlim.EnterReadLock(); + foreach (string str in _tempBlocked.Keys) + { + if ( + Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), + _tempBlocked[str]) > 0) + removes.Add(str); + } + _blockLockSlim.ExitReadLock(); + lock (_deeperInspection) + { + _blockLockSlim.EnterWriteLock(); + for (int i = 0; i < removes.Count; i++) + { + _tempBlocked.Remove(removes[i]); + _deeperInspection.Remove(removes[i]); + _sessions.Remove(removes[i]); + } + _blockLockSlim.ExitWriteLock(); + } + foreach (string str in removes) + { + m_log.InfoFormat("[{0}] client: {1} is no longer blocked.", + _options.ReportingName, str); + } + _blockLockSlim.EnterReadLock(); + if (_tempBlocked.Count > 0) + _forgetTimer.Enabled = true; + _blockLockSlim.ExitReadLock(); + }; + + _forgetTimer.Interval = _options.ForgetTimeSpan.TotalMilliseconds; + } + + /// + /// Given a string Key, Returns if that context is blocked + /// + /// A Key identifying the context + /// bool Yes or No, True or False for blocked + public bool IsBlocked(string key) + { + bool ret = false; + _blockLockSlim.EnterReadLock(); + ret = _tempBlocked.ContainsKey(key); + _blockLockSlim.ExitReadLock(); + return ret; + } + + /// + /// Process the velocity of this context + /// + /// + /// + /// + public bool Process(string key, string endpoint) + { + if (_options.MaxRequestsInTimeframe < 1 || _options.RequestTimeSpan.TotalMilliseconds < 1) + return true; + + string clientstring = key; + + _blockLockSlim.EnterReadLock(); + if (_tempBlocked.ContainsKey(clientstring)) + { + _blockLockSlim.ExitReadLock(); + + if (_options.ThrottledAction == ThrottleAction.DoThrottledMethod) + return false; + else + throw new System.Security.SecurityException("Throttled"); + } + + _blockLockSlim.ExitReadLock(); + + lock (_generalRequestTimes) + _generalRequestTimes.Put(Util.EnvironmentTickCount()); + + if (_options.MaxConcurrentSessions > 0) + { + int sessionscount = 0; + + _sessionLockSlim.EnterReadLock(); + if (_sessions.ContainsKey(key)) + sessionscount = _sessions[key]; + _sessionLockSlim.ExitReadLock(); + + if (sessionscount > _options.MaxConcurrentSessions) + { + // Add to blocking and cleanup methods + lock (_deeperInspection) + { + _blockLockSlim.EnterWriteLock(); + if (!_tempBlocked.ContainsKey(clientstring)) + { + _tempBlocked.Add(clientstring, + Util.EnvironmentTickCount() + + (int) _options.ForgetTimeSpan.TotalMilliseconds); + _forgetTimer.Enabled = true; + m_log.WarnFormat("[{0}]: client: {1} is blocked for {2} milliseconds based on concurrency, X-ForwardedForAllowed status is {3}, endpoint:{4}", _options.ReportingName, clientstring, _options.ForgetTimeSpan.TotalMilliseconds, _options.AllowXForwardedFor, endpoint); + + } + else + _tempBlocked[clientstring] = Util.EnvironmentTickCount() + + (int) _options.ForgetTimeSpan.TotalMilliseconds; + _blockLockSlim.ExitWriteLock(); + + } + + + } + else + ProcessConcurrency(key, endpoint); + } + if (_generalRequestTimes.Size == _generalRequestTimes.Capacity && + (Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), _generalRequestTimes.Get()) < + _options.RequestTimeSpan.TotalMilliseconds)) + { + //Trigger deeper inspection + if (DeeperInspection(key, endpoint)) + return true; + if (_options.ThrottledAction == ThrottleAction.DoThrottledMethod) + return false; + else + throw new System.Security.SecurityException("Throttled"); + } + return true; + } + private void ProcessConcurrency(string key, string endpoint) + { + _sessionLockSlim.EnterWriteLock(); + if (_sessions.ContainsKey(key)) + _sessions[key] = _sessions[key] + 1; + else + _sessions.Add(key,1); + _sessionLockSlim.ExitWriteLock(); + } + public void ProcessEnd(string key, string endpoint) + { + _sessionLockSlim.EnterWriteLock(); + if (_sessions.ContainsKey(key)) + { + _sessions[key]--; + if (_sessions[key] <= 0) + _sessions.Remove(key); + } + else + _sessions.Add(key, 1); + + _sessionLockSlim.ExitWriteLock(); + } + + /// + /// At this point, the rate limiting code needs to track 'per user' velocity. + /// + /// Context Key, string representing a rate limiting context + /// + /// + private bool DeeperInspection(string key, string endpoint) + { + lock (_deeperInspection) + { + string clientstring = key; + + + if (_deeperInspection.ContainsKey(clientstring)) + { + _deeperInspection[clientstring].Put(Util.EnvironmentTickCount()); + if (_deeperInspection[clientstring].Size == _deeperInspection[clientstring].Capacity && + (Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), _deeperInspection[clientstring].Get()) < + _options.RequestTimeSpan.TotalMilliseconds)) + { + //Looks like we're over the limit + _blockLockSlim.EnterWriteLock(); + if (!_tempBlocked.ContainsKey(clientstring)) + _tempBlocked.Add(clientstring, Util.EnvironmentTickCount() + (int)_options.ForgetTimeSpan.TotalMilliseconds); + else + _tempBlocked[clientstring] = Util.EnvironmentTickCount() + (int)_options.ForgetTimeSpan.TotalMilliseconds; + _blockLockSlim.ExitWriteLock(); + + m_log.WarnFormat("[{0}]: client: {1} is blocked for {2} milliseconds, X-ForwardedForAllowed status is {3}, endpoint:{4}", _options.ReportingName, clientstring, _options.ForgetTimeSpan.TotalMilliseconds, _options.AllowXForwardedFor, endpoint); + + return false; + } + //else + // return true; + } + else + { + _deeperInspection.Add(clientstring, new CircularBuffer(_options.MaxRequestsInTimeframe + 1, true)); + _deeperInspection[clientstring].Put(Util.EnvironmentTickCount()); + _forgetTimer.Enabled = true; + } + + } + return true; + } + + } + + + public class BasicDosProtectorOptions + { + public int MaxRequestsInTimeframe; + public TimeSpan RequestTimeSpan; + public TimeSpan ForgetTimeSpan; + public bool AllowXForwardedFor; + public string ReportingName = "BASICDOSPROTECTOR"; + public BasicDOSProtector.ThrottleAction ThrottledAction = BasicDOSProtector.ThrottleAction.DoThrottledMethod; + public int MaxConcurrentSessions; + } +} diff --git a/OpenSim/Framework/BlockingQueue.cs b/OpenSim/Framework/BlockingQueue.cs index 365816109b..3e90fac569 100644 --- a/OpenSim/Framework/BlockingQueue.cs +++ b/OpenSim/Framework/BlockingQueue.cs @@ -58,7 +58,7 @@ namespace OpenSim.Framework { lock (m_queueSync) { - if (m_queue.Count < 1 && m_pqueue.Count < 1) + while (m_queue.Count < 1 && m_pqueue.Count < 1) { Monitor.Wait(m_queueSync); } @@ -76,9 +76,10 @@ namespace OpenSim.Framework { lock (m_queueSync) { - if (m_queue.Count < 1 && m_pqueue.Count < 1) + bool success = true; + while (m_queue.Count < 1 && m_pqueue.Count < 1 && success) { - Monitor.Wait(m_queueSync, msTimeout); + success = Monitor.Wait(m_queueSync, msTimeout); } if (m_pqueue.Count > 0) @@ -89,8 +90,17 @@ namespace OpenSim.Framework } } + /// + /// Indicate whether this queue contains the given item. + /// + /// + /// This method is not thread-safe. Do not rely on the result without consistent external locking. + /// public bool Contains(T item) { + if (m_queue.Count < 1 && m_pqueue.Count < 1) + return false; + lock (m_queueSync) { if (m_pqueue.Contains(item)) @@ -99,16 +109,28 @@ namespace OpenSim.Framework } } + /// + /// Return a count of the number of requests on this queue. + /// + /// + /// This method is not thread-safe. Do not rely on the result without consistent external locking. + /// public int Count() { - lock (m_queueSync) - { - return m_queue.Count+m_pqueue.Count; - } + return m_queue.Count + m_pqueue.Count; } + /// + /// Return the array of items on this queue. + /// + /// + /// This method is not thread-safe. Do not rely on the result without consistent external locking. + /// public T[] GetQueueArray() { + if (m_queue.Count < 1 && m_pqueue.Count < 1) + return new T[0]; + lock (m_queueSync) { return m_queue.ToArray(); diff --git a/OpenSim/Framework/CachedTextureEventArg.cs b/OpenSim/Framework/CachedTextureEventArg.cs new file mode 100644 index 0000000000..239fc566a9 --- /dev/null +++ b/OpenSim/Framework/CachedTextureEventArg.cs @@ -0,0 +1,46 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Text; +using OpenMetaverse; + +namespace OpenSim.Framework +{ + public class CachedTextureRequestArg + { + public int BakedTextureIndex; + public UUID WearableHashID; + } + + public class CachedTextureResponseArg + { + public int BakedTextureIndex; + public UUID BakedTextureID; + public String HostName; + } +} diff --git a/OpenSim/Framework/ChildAgentDataUpdate.cs b/OpenSim/Framework/ChildAgentDataUpdate.cs index 8c32734980..2a8e67dd61 100644 --- a/OpenSim/Framework/ChildAgentDataUpdate.cs +++ b/OpenSim/Framework/ChildAgentDataUpdate.cs @@ -171,9 +171,10 @@ namespace OpenSim.Framework /// Soon to be decommissioned /// /// - public void CopyFrom(ChildAgentDataUpdate cAgent) + public void CopyFrom(ChildAgentDataUpdate cAgent, UUID sid) { AgentID = new UUID(cAgent.AgentID); + SessionID = sid; // next: ??? Size = new Vector3(); @@ -291,7 +292,13 @@ namespace OpenSim.Framework public Vector3 AtAxis; public Vector3 LeftAxis; public Vector3 UpAxis; - public bool ChangedGrid; + + /// + /// Signal on a V2 teleport that Scene.IncomingChildAgentDataUpdate(AgentData ad) should wait for the + /// scene presence to become root (triggered when the viewer sends a CompleteAgentMovement UDP packet after + /// establishing the connection triggered by it's receipt of a TeleportFinish EQ message). + /// + public bool SenderWantsToWaitForRoot; public float Far; public float Aspect; @@ -362,8 +369,9 @@ namespace OpenSim.Framework args["left_axis"] = OSD.FromString(LeftAxis.ToString()); args["up_axis"] = OSD.FromString(UpAxis.ToString()); - - args["changed_grid"] = OSD.FromBoolean(ChangedGrid); + //backwards compatibility + args["changed_grid"] = OSD.FromBoolean(SenderWantsToWaitForRoot); + args["wait_for_root"] = OSD.FromBoolean(SenderWantsToWaitForRoot); args["far"] = OSD.FromReal(Far); args["aspect"] = OSD.FromReal(Aspect); @@ -536,8 +544,8 @@ namespace OpenSim.Framework if (args["up_axis"] != null) Vector3.TryParse(args["up_axis"].AsString(), out AtAxis); - if (args["changed_grid"] != null) - ChangedGrid = args["changed_grid"].AsBoolean(); + if (args.ContainsKey("wait_for_root") && args["wait_for_root"] != null) + SenderWantsToWaitForRoot = args["wait_for_root"].AsBoolean(); if (args["far"] != null) Far = (float)(args["far"].AsReal()); diff --git a/OpenSim/Framework/CircularBuffer.cs b/OpenSim/Framework/CircularBuffer.cs new file mode 100644 index 0000000000..e919337a70 --- /dev/null +++ b/OpenSim/Framework/CircularBuffer.cs @@ -0,0 +1,312 @@ +/* +Copyright (c) 2012, Alex Regueiro +All rights reserved. +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Threading; + +namespace OpenSim.Framework +{ + public class CircularBuffer : ICollection, IEnumerable, ICollection, IEnumerable + { + private int capacity; + private int size; + private int head; + private int tail; + private T[] buffer; + + [NonSerialized()] + private object syncRoot; + + public CircularBuffer(int capacity) + : this(capacity, false) + { + } + + public CircularBuffer(int capacity, bool allowOverflow) + { + if (capacity < 0) + throw new ArgumentException("Needs to have at least 1","capacity"); + + this.capacity = capacity; + size = 0; + head = 0; + tail = 0; + buffer = new T[capacity]; + AllowOverflow = allowOverflow; + } + + public bool AllowOverflow + { + get; + set; + } + + public int Capacity + { + get { return capacity; } + set + { + if (value == capacity) + return; + + if (value < size) + throw new ArgumentOutOfRangeException("value","Capacity is too small."); + + var dst = new T[value]; + if (size > 0) + CopyTo(dst); + buffer = dst; + + capacity = value; + } + } + + public int Size + { + get { return size; } + } + + public bool Contains(T item) + { + int bufferIndex = head; + var comparer = EqualityComparer.Default; + for (int i = 0; i < size; i++, bufferIndex++) + { + if (bufferIndex == capacity) + bufferIndex = 0; + + if (item == null && buffer[bufferIndex] == null) + return true; + else if ((buffer[bufferIndex] != null) && + comparer.Equals(buffer[bufferIndex], item)) + return true; + } + + return false; + } + + public void Clear() + { + size = 0; + head = 0; + tail = 0; + } + + public int Put(T[] src) + { + return Put(src, 0, src.Length); + } + + public int Put(T[] src, int offset, int count) + { + if (!AllowOverflow && count > capacity - size) + throw new InvalidOperationException("Buffer Overflow"); + + int srcIndex = offset; + for (int i = 0; i < count; i++, tail++, srcIndex++) + { + if (tail == capacity) + tail = 0; + buffer[tail] = src[srcIndex]; + } + size = Math.Min(size + count, capacity); + return count; + } + + public void Put(T item) + { + if (!AllowOverflow && size == capacity) + throw new InvalidOperationException("Buffer Overflow"); + + buffer[tail] = item; + if (++tail == capacity) + tail = 0; + size++; + } + + public void Skip(int count) + { + head += count; + if (head >= capacity) + head -= capacity; + } + + public T[] Get(int count) + { + var dst = new T[count]; + Get(dst); + return dst; + } + + public int Get(T[] dst) + { + return Get(dst, 0, dst.Length); + } + + public int Get(T[] dst, int offset, int count) + { + int realCount = Math.Min(count, size); + int dstIndex = offset; + for (int i = 0; i < realCount; i++, head++, dstIndex++) + { + if (head == capacity) + head = 0; + dst[dstIndex] = buffer[head]; + } + size -= realCount; + return realCount; + } + + public T Get() + { + if (size == 0) + throw new InvalidOperationException("Buffer Empty"); + + var item = buffer[head]; + if (++head == capacity) + head = 0; + size--; + return item; + } + + public void CopyTo(T[] array) + { + CopyTo(array, 0); + } + + public void CopyTo(T[] array, int arrayIndex) + { + CopyTo(0, array, arrayIndex, size); + } + + public void CopyTo(int index, T[] array, int arrayIndex, int count) + { + if (count > size) + throw new ArgumentOutOfRangeException("count", "Count Too Large"); + + int bufferIndex = head; + for (int i = 0; i < count; i++, bufferIndex++, arrayIndex++) + { + if (bufferIndex == capacity) + bufferIndex = 0; + array[arrayIndex] = buffer[bufferIndex]; + } + } + + public IEnumerator GetEnumerator() + { + int bufferIndex = head; + for (int i = 0; i < size; i++, bufferIndex++) + { + if (bufferIndex == capacity) + bufferIndex = 0; + + yield return buffer[bufferIndex]; + } + } + + public T[] GetBuffer() + { + return buffer; + } + + public T[] ToArray() + { + var dst = new T[size]; + CopyTo(dst); + return dst; + } + + #region ICollection Members + + int ICollection.Count + { + get { return Size; } + } + + bool ICollection.IsReadOnly + { + get { return false; } + } + + void ICollection.Add(T item) + { + Put(item); + } + + bool ICollection.Remove(T item) + { + if (size == 0) + return false; + + Get(); + return true; + } + + #endregion + + #region IEnumerable Members + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #endregion + + #region ICollection Members + + int ICollection.Count + { + get { return Size; } + } + + bool ICollection.IsSynchronized + { + get { return false; } + } + + object ICollection.SyncRoot + { + get + { + if (syncRoot == null) + Interlocked.CompareExchange(ref syncRoot, new object(), null); + return syncRoot; + } + } + + void ICollection.CopyTo(Array array, int arrayIndex) + { + CopyTo((T[])array, arrayIndex); + } + + #endregion + + #region IEnumerable Members + + IEnumerator IEnumerable.GetEnumerator() + { + return (IEnumerator)GetEnumerator(); + } + + #endregion + } +} diff --git a/OpenSim/Framework/ClientInfo.cs b/OpenSim/Framework/ClientInfo.cs index 62acb70566..9021315084 100644 --- a/OpenSim/Framework/ClientInfo.cs +++ b/OpenSim/Framework/ClientInfo.cs @@ -33,12 +33,13 @@ namespace OpenSim.Framework { public class ClientInfo { - public AgentCircuitData agentcircuit; + public readonly DateTime StartedTime = DateTime.Now; + public AgentCircuitData agentcircuit = null; public Dictionary needAck; - public List out_packets; - public Dictionary pendingAcks; + public List out_packets = new List(); + public Dictionary pendingAcks = new Dictionary(); public EndPoint proxyEP; public uint sequence; @@ -53,5 +54,9 @@ namespace OpenSim.Framework public int assetThrottle; public int textureThrottle; public int totalThrottle; + + public Dictionary SyncRequests = new Dictionary(); + public Dictionary AsyncRequests = new Dictionary(); + public Dictionary GenericRequests = new Dictionary(); } } diff --git a/OpenSim/Framework/Communications/Properties/AssemblyInfo.cs b/OpenSim/Framework/Communications/Properties/AssemblyInfo.cs index df8eb520e1..09611fadf7 100644 --- a/OpenSim/Framework/Communications/Properties/AssemblyInfo.cs +++ b/OpenSim/Framework/Communications/Properties/AssemblyInfo.cs @@ -61,5 +61,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: -[assembly : AssemblyVersion("0.7.6.*")] +[assembly : AssemblyVersion("0.8.0.*")] diff --git a/OpenSim/Framework/ConfigSettings.cs b/OpenSim/Framework/ConfigSettings.cs index 002a37170e..108a3e4407 100644 --- a/OpenSim/Framework/ConfigSettings.cs +++ b/OpenSim/Framework/ConfigSettings.cs @@ -31,7 +31,6 @@ namespace OpenSim.Framework { public string PhysicsEngine { get; set; } public string MeshEngineName { get; set; } - public string StorageDll { get; set; } public string ClientstackDll { get; set; } public string LibrariesXMLFile { get; set; } diff --git a/OpenSim/Framework/Configuration/HTTP/Properties/AssemblyInfo.cs b/OpenSim/Framework/Configuration/HTTP/Properties/AssemblyInfo.cs index 3ef9682a95..05a2e0ec91 100644 --- a/OpenSim/Framework/Configuration/HTTP/Properties/AssemblyInfo.cs +++ b/OpenSim/Framework/Configuration/HTTP/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; // Build Number // Revision // -[assembly: AssemblyVersion("0.7.6.*")] +[assembly: AssemblyVersion("0.8.0.*")] diff --git a/OpenSim/Framework/Configuration/XML/Properties/AssemblyInfo.cs b/OpenSim/Framework/Configuration/XML/Properties/AssemblyInfo.cs index cbdffeb78c..d928a94685 100644 --- a/OpenSim/Framework/Configuration/XML/Properties/AssemblyInfo.cs +++ b/OpenSim/Framework/Configuration/XML/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; // Build Number // Revision // -[assembly: AssemblyVersion("0.7.6.*")] +[assembly: AssemblyVersion("0.8.0.*")] diff --git a/OpenSim/Framework/Configuration/XML/XmlConfiguration.cs b/OpenSim/Framework/Configuration/XML/XmlConfiguration.cs index 43162fc227..3152a7d4d6 100644 --- a/OpenSim/Framework/Configuration/XML/XmlConfiguration.cs +++ b/OpenSim/Framework/Configuration/XML/XmlConfiguration.cs @@ -121,7 +121,7 @@ namespace OpenSim.Framework.Configuration.XML public void Commit() { - if (fileName == null || fileName == String.Empty) + if (string.IsNullOrEmpty(fileName)) return; if (!Directory.Exists(Util.configDir())) diff --git a/OpenSim/Framework/Console/AssemblyInfo.cs b/OpenSim/Framework/Console/AssemblyInfo.cs index c618454a7a..ba590257c1 100644 --- a/OpenSim/Framework/Console/AssemblyInfo.cs +++ b/OpenSim/Framework/Console/AssemblyInfo.cs @@ -55,4 +55,4 @@ using System.Runtime.InteropServices; // You can specify all values by your own or you can build default build and revision // numbers with the '*' character (the default): -[assembly : AssemblyVersion("0.7.6.*")] +[assembly : AssemblyVersion("0.8.0.*")] diff --git a/OpenSim/Framework/Console/ConsoleUtil.cs b/OpenSim/Framework/Console/ConsoleUtil.cs index 97a86a8099..794bfaf8dd 100644 --- a/OpenSim/Framework/Console/ConsoleUtil.cs +++ b/OpenSim/Framework/Console/ConsoleUtil.cs @@ -156,12 +156,32 @@ namespace OpenSim.Framework.Console } /// - /// Convert a minimum vector input from the console to an OpenMetaverse.Vector3 + /// Convert a console integer to an int, automatically complaining if a console is given. /// /// Can be null if no console is available. /// /param> /// /// + public static bool TryParseConsoleBool(ICommandConsole console, string rawConsoleString, out bool b) + { + if (!bool.TryParse(rawConsoleString, out b)) + { + if (console != null) + console.OutputFormat("ERROR: {0} is not a true or false value", rawConsoleString); + + return false; + } + + return true; + } + + /// + /// Convert a console integer to an int, automatically complaining if a console is given. + /// + /// Can be null if no console is available. + /// /param> + /// + /// public static bool TryParseConsoleInt(ICommandConsole console, string rawConsoleInt, out int i) { if (!int.TryParse(rawConsoleInt, out i)) @@ -174,6 +194,31 @@ namespace OpenSim.Framework.Console return true; } + + /// + /// Convert a console integer to a natural int, automatically complaining if a console is given. + /// + /// Can be null if no console is available. + /// /param> + /// + /// + public static bool TryParseConsoleNaturalInt(ICommandConsole console, string rawConsoleInt, out int i) + { + if (TryParseConsoleInt(console, rawConsoleInt, out i)) + { + if (i < 0) + { + if (console != null) + console.OutputFormat("ERROR: {0} is not a positive integer", rawConsoleInt); + + return false; + } + + return true; + } + + return false; + } /// /// Convert a minimum vector input from the console to an OpenMetaverse.Vector3 diff --git a/OpenSim/Framework/Console/LocalConsole.cs b/OpenSim/Framework/Console/LocalConsole.cs index d41481fb1c..a967db6271 100644 --- a/OpenSim/Framework/Console/LocalConsole.cs +++ b/OpenSim/Framework/Console/LocalConsole.cs @@ -420,6 +420,21 @@ namespace OpenSim.Framework.Console SetCursorLeft(0); m_cursorYPosition = SetCursorTop(m_cursorYPosition); + if (m_echo) + System.Console.Write("{0}{1} ", prompt, m_commandLine); + else + System.Console.Write("{0}", prompt); + + break; + case ConsoleKey.Delete: + if (m_cursorXPosition == m_commandLine.Length) + break; + + m_commandLine.Remove(m_cursorXPosition, 1); + + SetCursorLeft(0); + m_cursorYPosition = SetCursorTop(m_cursorYPosition); + if (m_echo) System.Console.Write("{0}{1} ", prompt, m_commandLine); else diff --git a/OpenSim/Framework/Console/RemoteConsole.cs b/OpenSim/Framework/Console/RemoteConsole.cs index 3e3c2b3758..8ad7b0daa9 100644 --- a/OpenSim/Framework/Console/RemoteConsole.cs +++ b/OpenSim/Framework/Console/RemoteConsole.cs @@ -234,7 +234,7 @@ namespace OpenSim.Framework.Console string uri = "/ReadResponses/" + sessionID.ToString() + "/"; m_Server.AddPollServiceHTTPHandler( - uri, new PollServiceEventArgs(null, HasEvents, GetEvents, NoEvents, sessionID,25000)); // 25 secs timeout + uri, new PollServiceEventArgs(null, uri, HasEvents, GetEvents, NoEvents, sessionID,25000)); // 25 secs timeout XmlDocument xmldoc = new XmlDocument(); XmlNode xmlnode = xmldoc.CreateNode(XmlNodeType.XmlDeclaration, diff --git a/OpenSim/Framework/Constants.cs b/OpenSim/Framework/Constants.cs index a2eb5ee18e..3ba264c7af 100644 --- a/OpenSim/Framework/Constants.cs +++ b/OpenSim/Framework/Constants.cs @@ -30,9 +30,18 @@ namespace OpenSim.Framework { public class Constants { + // 'RegionSize' is the legacy region size. + // DO NOT USE THIS FOR ANY NEW CODE. Use Scene.RegionInfo.RegionSize[XYZ] as a region might not + // be the legacy region size. public const uint RegionSize = 256; public const uint RegionHeight = 4096; - public const byte TerrainPatchSize = 16; + // This could be a parameters but, really, a region of greater than this is pretty unmanageable + public const uint MaximumRegionSize = 8192; + + // Since terrain is stored in 16x16 heights, regions must be a multiple of this number and that is the minimum + public const int MinRegionSize = 16; + public const int TerrainPatchSize = 16; + public const string DefaultTexture = "89556747-24cb-43ed-920b-47caed15465f"; public enum EstateAccessCodex : uint diff --git a/OpenSim/Framework/DAMap.cs b/OpenSim/Framework/DAMap.cs index df4a6bc4a7..4995a92de0 100644 --- a/OpenSim/Framework/DAMap.cs +++ b/OpenSim/Framework/DAMap.cs @@ -29,10 +29,12 @@ using System; using System.Collections; using System.Collections.Generic; using System.IO; +using System.Reflection; using System.Text; using System.Xml; using System.Xml.Schema; using System.Xml.Serialization; +using log4net; using OpenMetaverse; using OpenMetaverse.StructuredData; @@ -48,13 +50,20 @@ namespace OpenSim.Framework /// within their data store. However, avoid storing large amounts of data because that /// would slow down database access. /// - public class DAMap : IDictionary, IXmlSerializable + public class DAMap : IXmlSerializable { - private static readonly int MIN_STORE_NAME_LENGTH = 4; +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - protected OSDMap m_map; - - public DAMap() { m_map = new OSDMap(); } + private static readonly int MIN_NAMESPACE_LENGTH = 4; + + private OSDMap m_map = new OSDMap(); + + // WARNING: this is temporary for experimentation only, it will be removed!!!! + public OSDMap TopLevelMap + { + get { return m_map; } + set { m_map = value; } + } public XmlSchema GetSchema() { return null; } @@ -64,39 +73,34 @@ namespace OpenSim.Framework map.ReadXml(rawXml); return map; } - + + public void ReadXml(XmlReader reader) + { + ReadXml(reader.ReadInnerXml()); + } + public void ReadXml(string rawXml) { // System.Console.WriteLine("Trying to deserialize [{0}]", rawXml); lock (this) + { m_map = (OSDMap)OSDParser.DeserializeLLSDXml(rawXml); - } - - // WARNING: this is temporary for experimentation only, it will be removed!!!! - public OSDMap TopLevelMap - { - get { return m_map; } - set { m_map = value; } - } - - - public void ReadXml(XmlReader reader) - { - ReadXml(reader.ReadInnerXml()); - } - - public string ToXml() - { - lock (this) - return OSDParser.SerializeLLSDXmlString(m_map); + SanitiseMap(this); + } } public void WriteXml(XmlWriter writer) { writer.WriteRaw(ToXml()); } - + + public string ToXml() + { + lock (this) + return OSDParser.SerializeLLSDXmlString(m_map); + } + public void CopyFrom(DAMap other) { // Deep copy @@ -104,7 +108,7 @@ namespace OpenSim.Framework string data = null; lock (other) { - if (other.Count > 0) + if (other.CountNamespaces > 0) { data = OSDParser.SerializeLLSDXmlString(other.m_map); } @@ -120,59 +124,132 @@ namespace OpenSim.Framework } /// - /// Returns the number of data stores. + /// Sanitise the map to remove any namespaces or stores that are not OSDMap. /// - public int Count { get { lock (this) { return m_map.Count; } } } - - public bool IsReadOnly { get { return false; } } - - /// - /// Returns the names of the data stores. - /// - public ICollection Keys { get { lock (this) { return m_map.Keys; } } } - - /// - /// Returns all the data stores. - /// - public ICollection Values + /// + /// + public static void SanitiseMap(DAMap daMap) { - get + List keysToRemove = null; + + OSDMap namespacesMap = daMap.m_map; + + foreach (string key in namespacesMap.Keys) { - lock (this) +// Console.WriteLine("Processing ns {0}", key); + if (!(namespacesMap[key] is OSDMap)) { - List stores = new List(m_map.Count); - foreach (OSD llsd in m_map.Values) - stores.Add((OSDMap)llsd); - return stores; + if (keysToRemove == null) + keysToRemove = new List(); + + keysToRemove.Add(key); } } + + if (keysToRemove != null) + { + foreach (string key in keysToRemove) + { +// Console.WriteLine ("Removing bad ns {0}", key); + namespacesMap.Remove(key); + } + } + + foreach (OSD nsOsd in namespacesMap.Values) + { + OSDMap nsOsdMap = (OSDMap)nsOsd; + keysToRemove = null; + + foreach (string key in nsOsdMap.Keys) + { + if (!(nsOsdMap[key] is OSDMap)) + { + if (keysToRemove == null) + keysToRemove = new List(); + + keysToRemove.Add(key); + } + } + + if (keysToRemove != null) + foreach (string key in keysToRemove) + nsOsdMap.Remove(key); + } } - + /// - /// Gets or sets one data store. + /// Get the number of namespaces /// - /// Store name - /// - public OSDMap this[string key] - { - get - { - OSD llsd; - + public int CountNamespaces { get { lock (this) { return m_map.Count; } } } + + /// + /// Get the number of stores. + /// + public int CountStores + { + get + { + int count = 0; + lock (this) { - if (m_map.TryGetValue(key, out llsd)) - return (OSDMap)llsd; - else - return null; + foreach (OSD osdNamespace in m_map) + { + count += ((OSDMap)osdNamespace).Count; + } } - } - - set + + return count; + } + } + + /// + /// Retrieve a Dynamic Attribute store + /// + /// namespace for the store - use "OpenSim" for in-core modules + /// name of the store within the namespace + /// an OSDMap representing the stored data, or null if not found + public OSDMap GetStore(string ns, string storeName) + { + OSD namespaceOsd; + + lock (this) { - ValidateKey(key); - lock (this) - m_map[key] = value; + if (m_map.TryGetValue(ns, out namespaceOsd)) + { + OSD store; + + if (((OSDMap)namespaceOsd).TryGetValue(storeName, out store)) + return (OSDMap)store; + } + } + + return null; + } + + /// + /// Saves a Dynamic attribute store + /// + /// namespace for the store - use "OpenSim" for in-core modules + /// name of the store within the namespace + /// an OSDMap representing the data to store + public void SetStore(string ns, string storeName, OSDMap store) + { + ValidateNamespace(ns); + OSDMap nsMap; + + lock (this) + { + if (!m_map.ContainsKey(ns)) + { + nsMap = new OSDMap(); + m_map[ns] = nsMap; + } + + nsMap = (OSDMap)m_map[ns]; + +// m_log.DebugFormat("[DA MAP]: Setting store to {0}:{1}", ns, storeName); + nsMap[storeName] = store; } } @@ -180,54 +257,46 @@ namespace OpenSim.Framework /// Validate the key used for storing separate data stores. /// /// - public static void ValidateKey(string key) + public static void ValidateNamespace(string ns) { - if (key.Length < MIN_STORE_NAME_LENGTH) - throw new Exception("Minimum store name length is " + MIN_STORE_NAME_LENGTH); + if (ns.Length < MIN_NAMESPACE_LENGTH) + throw new Exception("Minimum namespace length is " + MIN_NAMESPACE_LENGTH); } - public bool ContainsKey(string key) + public bool ContainsStore(string ns, string storeName) { - lock (this) - return m_map.ContainsKey(key); - } + OSD namespaceOsd; - public void Add(string key, OSDMap store) - { - ValidateKey(key); - lock (this) - m_map.Add(key, store); - } - - public void Add(KeyValuePair kvp) - { - ValidateKey(kvp.Key); - lock (this) - m_map.Add(kvp.Key, kvp.Value); - } - - public bool Remove(string key) - { - lock (this) - return m_map.Remove(key); - } - - public bool TryGetValue(string key, out OSDMap store) - { lock (this) { - OSD llsd; - if (m_map.TryGetValue(key, out llsd)) + if (m_map.TryGetValue(ns, out namespaceOsd)) { - store = (OSDMap)llsd; - return true; - } - else - { - store = null; - return false; + return ((OSDMap)namespaceOsd).ContainsKey(storeName); } } + + return false; + } + + public bool TryGetStore(string ns, string storeName, out OSDMap store) + { + OSD namespaceOsd; + + lock (this) + { + if (m_map.TryGetValue(ns, out namespaceOsd)) + { + OSD storeOsd; + + bool result = ((OSDMap)namespaceOsd).TryGetValue(storeName, out storeOsd); + store = (OSDMap)storeOsd; + + return result; + } + } + + store = null; + return false; } public void Clear() @@ -235,39 +304,25 @@ namespace OpenSim.Framework lock (this) m_map.Clear(); } - - public bool Contains(KeyValuePair kvp) + + public bool RemoveStore(string ns, string storeName) { + OSD namespaceOsd; + lock (this) - return m_map.ContainsKey(kvp.Key); - } + { + if (m_map.TryGetValue(ns, out namespaceOsd)) + { + OSDMap namespaceOsdMap = (OSDMap)namespaceOsd; + namespaceOsdMap.Remove(storeName); - public void CopyTo(KeyValuePair[] array, int index) - { - throw new NotImplementedException(); - } + // Don't keep empty namespaces around + if (namespaceOsdMap.Count <= 0) + m_map.Remove(ns); + } + } - public bool Remove(KeyValuePair kvp) - { - lock (this) - return m_map.Remove(kvp.Key); - } - - public System.Collections.IDictionaryEnumerator GetEnumerator() - { - lock (this) - return m_map.GetEnumerator(); - } - - IEnumerator> IEnumerable>.GetEnumerator() - { - return null; - } - - IEnumerator IEnumerable.GetEnumerator() - { - lock (this) - return m_map.GetEnumerator(); - } + return false; + } } } \ No newline at end of file diff --git a/OpenSim/Framework/DOMap.cs b/OpenSim/Framework/DOMap.cs index 755e129a13..f5b650b375 100644 --- a/OpenSim/Framework/DOMap.cs +++ b/OpenSim/Framework/DOMap.cs @@ -42,22 +42,22 @@ namespace OpenSim.Framework /// This class stores and retrieves dynamic objects. /// /// - /// Experimental - DO NOT USE. + /// Experimental - DO NOT USE. Does not yet have namespace support. /// public class DOMap { private IDictionary m_map; - public void Add(string key, object dynObj) + public void Add(string ns, string objName, object dynObj) { - DAMap.ValidateKey(key); + DAMap.ValidateNamespace(ns); lock (this) { if (m_map == null) m_map = new Dictionary(); - m_map.Add(key, dynObj); + m_map.Add(objName, dynObj); } } diff --git a/OpenSim/Framework/EstateSettings.cs b/OpenSim/Framework/EstateSettings.cs index e03750bcc5..5ddbd6161e 100644 --- a/OpenSim/Framework/EstateSettings.cs +++ b/OpenSim/Framework/EstateSettings.cs @@ -430,6 +430,5 @@ namespace OpenSim.Framework { return l_EstateGroups.Contains(groupID); } - } } diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index ad3471ae62..22cc79da33 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -65,6 +65,7 @@ namespace OpenSim.Framework public delegate void NetworkStats(int inPackets, int outPackets, int unAckedBytes); public delegate void SetAppearance(IClientAPI remoteClient, Primitive.TextureEntry textureEntry, byte[] visualParams, Vector3 AvSize, WearableCacheItem[] CacheItems); + public delegate void CachedTextureRequest(IClientAPI remoteClient, int serial, List cachedTextureRequest); public delegate void StartAnim(IClientAPI remoteClient, UUID animID); @@ -789,6 +790,7 @@ namespace OpenSim.Framework event EstateChangeInfo OnEstateChangeInfo; event EstateManageTelehub OnEstateManageTelehub; // [Obsolete("LLClientView Specific.")] + event CachedTextureRequest OnCachedTextureRequest; event SetAppearance OnSetAppearance; // [Obsolete("LLClientView Specific - Replace and rename OnAvatarUpdate. Difference from SetAppearance?")] event AvatarNowWearing OnAvatarNowWearing; @@ -832,6 +834,8 @@ namespace OpenSim.Framework /// event UpdateAgent OnAgentUpdate; + event UpdateAgent OnAgentCameraUpdate; + event AgentRequestSit OnAgentRequestSit; event AgentSit OnAgentSit; event AvatarPickerRequest OnAvatarPickerRequest; @@ -1100,14 +1104,15 @@ namespace OpenSim.Framework /// void SendAppearance(UUID agentID, byte[] visualParams, byte[] textureEntry); + void SendCachedTextureResponse(ISceneEntity avatar, int serial, List cachedTextures); + void SendStartPingCheck(byte seq); /// /// Tell the client that an object has been deleted /// - /// /// - void SendKillObject(ulong regionHandle, List localID); + void SendKillObject(List localID); void SendAnimations(UUID[] animID, int[] seqs, UUID sourceAgentId, UUID[] objectIDs); void SendRegionHandshake(RegionInfo regionInfo, RegionHandshakeArgs args); @@ -1486,7 +1491,7 @@ namespace OpenSim.Framework void SendChangeUserRights(UUID agentID, UUID friendID, int rights); void SendTextBoxRequest(string message, int chatChannel, string objectname, UUID ownerID, string ownerFirstName, string ownerLastName, UUID objectId); - void StopFlying(ISceneEntity presence); + void SendAgentTerseUpdate(ISceneEntity presence); void SendPlacesReply(UUID queryID, UUID transactionID, PlacesReplyData[] data); } diff --git a/OpenSim/Framework/IImprovedAssetCache.cs b/OpenSim/Framework/IImprovedAssetCache.cs index a0b8b55aaa..a853e9015d 100644 --- a/OpenSim/Framework/IImprovedAssetCache.cs +++ b/OpenSim/Framework/IImprovedAssetCache.cs @@ -31,10 +31,34 @@ namespace OpenSim.Framework { public interface IImprovedAssetCache { + /// + /// Cache the specified asset. + /// + /// void Cache(AssetBase asset); + + /// + /// Get an asset by its id. + /// + /// + /// null if the asset does not exist. AssetBase Get(string id); + + /// + /// Check whether an asset with the specified id exists in the cache. + /// + /// bool Check(string id); + + /// + /// Expire an asset from the cache. + /// + /// void Expire(string id); + + /// + /// Clear the cache. + /// void Clear(); } -} +} \ No newline at end of file diff --git a/OpenSim/Framework/IPeople.cs b/OpenSim/Framework/IPeople.cs new file mode 100644 index 0000000000..b88e1039e6 --- /dev/null +++ b/OpenSim/Framework/IPeople.cs @@ -0,0 +1,47 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using OpenMetaverse; + +namespace OpenSim.Framework +{ + public class UserData + { + public UUID Id { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + public string HomeURL { get; set; } + public Dictionary ServerURLs { get; set; } + } + + public interface IPeople + { + List GetUserData(string query, int page_size, int page_number); + } +} \ No newline at end of file diff --git a/OpenSim/Framework/IScene.cs b/OpenSim/Framework/IScene.cs index 87ec99ef60..e1b6d1e1ad 100644 --- a/OpenSim/Framework/IScene.cs +++ b/OpenSim/Framework/IScene.cs @@ -86,24 +86,26 @@ namespace OpenSim.Framework event restart OnRestart; /// - /// Add a new client and create a presence for it. All clients except initial login clients will starts off as a child agent + /// Add a new agent with an attached client. All agents except initial login clients will starts off as a child agent /// - the later agent crossing will promote it to a root agent. /// /// /// The type of agent to add. /// /// The scene agent if the new client was added or if an agent that already existed. - ISceneAgent AddNewClient(IClientAPI client, PresenceType type); + ISceneAgent AddNewAgent(IClientAPI client, PresenceType type); /// - /// Remove the given client from the scene. + /// Tell a single agent to disconnect from the region. /// /// - /// Close the neighbour child agents associated with this client. - void RemoveClient(UUID agentID, bool closeChildAgents); + /// + /// Force the agent to close even if it might be in the middle of some other operation. You do not want to + /// force unless you are absolutely sure that the agent is dead and a normal close is not working. + /// + bool CloseAgent(UUID agentID, bool force); void Restart(); - //RegionInfo OtherRegionUp(RegionInfo thisRegion); string GetSimulatorVersion(); @@ -136,5 +138,10 @@ namespace OpenSim.Framework ISceneObject DeserializeObject(string representation); bool CheckClient(UUID agentID, System.Net.IPEndPoint ep); + + /// + /// Start the scene and associated scripts within it. + /// + void Start(); } -} +} \ No newline at end of file diff --git a/OpenSim/Framework/InventoryItemBase.cs b/OpenSim/Framework/InventoryItemBase.cs index 3d45e76698..558dfd0e4c 100644 --- a/OpenSim/Framework/InventoryItemBase.cs +++ b/OpenSim/Framework/InventoryItemBase.cs @@ -122,7 +122,7 @@ namespace OpenSim.Framework { get { - if (m_creatorData != null && m_creatorData != string.Empty) + if (!string.IsNullOrEmpty(m_creatorData)) return m_creatorId + ';' + m_creatorData; else return m_creatorId; diff --git a/OpenSim/Framework/Location.cs b/OpenSim/Framework/Location.cs index 9504e03547..0b888347e7 100644 --- a/OpenSim/Framework/Location.cs +++ b/OpenSim/Framework/Location.cs @@ -33,10 +33,10 @@ namespace OpenSim.Framework [Serializable] public class Location : ICloneable { - private readonly int m_x; - private readonly int m_y; + private readonly uint m_x; + private readonly uint m_y; - public Location(int x, int y) + public Location(uint x, uint y) { m_x = x; m_y = y; @@ -44,21 +44,21 @@ namespace OpenSim.Framework public Location(ulong regionHandle) { - m_x = (int) regionHandle; - m_y = (int) (regionHandle >> 32); + m_x = (uint)(regionHandle >> 32); + m_y = (uint)(regionHandle & (ulong)uint.MaxValue); } public ulong RegionHandle { - get { return Utils.UIntsToLong((uint)m_x, (uint)m_y); } + get { return Utils.UIntsToLong(m_x, m_y); } } - public int X + public uint X { get { return m_x; } } - public int Y + public uint Y { get { return m_y; } } diff --git a/OpenSim/Framework/Monitoring/BaseStatsCollector.cs b/OpenSim/Framework/Monitoring/BaseStatsCollector.cs index 23dba09c13..96536e8e00 100644 --- a/OpenSim/Framework/Monitoring/BaseStatsCollector.cs +++ b/OpenSim/Framework/Monitoring/BaseStatsCollector.cs @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) Contributors, http://opensimulator.org/ * See CONTRIBUTORS.TXT for a full list of copyright holders. * @@ -44,16 +44,16 @@ namespace OpenSim.Framework.Monitoring sb.Append("MEMORY STATISTICS"); sb.Append(Environment.NewLine); sb.AppendFormat( - "Allocated to OpenSim objects: {0} MB\n", - Math.Round(GC.GetTotalMemory(false) / 1024.0 / 1024.0)); + "Heap allocated to OpenSim : {0} MB\n", + Math.Round(GC.GetTotalMemory(false) / 1024.0 / 1024.0)); sb.AppendFormat( - "OpenSim last object memory churn : {0} MB/s\n", - Math.Round((MemoryWatchdog.LastMemoryChurn * 1000) / 1024.0 / 1024, 3)); + "Last heap allocation rate : {0} MB/s\n", + Math.Round((MemoryWatchdog.LastHeapAllocationRate * 1000) / 1024.0 / 1024, 3)); sb.AppendFormat( - "OpenSim average object memory churn : {0} MB/s\n", - Math.Round((MemoryWatchdog.AverageMemoryChurn * 1000) / 1024.0 / 1024, 3)); + "Average heap allocation rate: {0} MB/s\n", + Math.Round((MemoryWatchdog.AverageHeapAllocationRate * 1000) / 1024.0 / 1024, 3)); Process myprocess = Process.GetCurrentProcess(); if (!myprocess.HasExited) diff --git a/OpenSim/Framework/Monitoring/Checks/Check.cs b/OpenSim/Framework/Monitoring/Checks/Check.cs new file mode 100644 index 0000000000..594386a93f --- /dev/null +++ b/OpenSim/Framework/Monitoring/Checks/Check.cs @@ -0,0 +1,118 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Text; + +namespace OpenSim.Framework.Monitoring +{ + public class Check + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public static readonly char[] DisallowedShortNameCharacters = { '.' }; + + /// + /// Category of this stat (e.g. cache, scene, etc). + /// + public string Category { get; private set; } + + /// + /// Containing name for this stat. + /// FIXME: In the case of a scene, this is currently the scene name (though this leaves + /// us with a to-be-resolved problem of non-unique region names). + /// + /// + /// The container. + /// + public string Container { get; private set; } + + /// + /// Action used to check whether alert should go off. + /// + /// + /// Should return true if check passes. False otherwise. + /// + public Func CheckFunc { get; private set; } + + /// + /// Message from the last failure, if any. If there is no message or no failure then will be null. + /// + /// + /// Should be set by the CheckFunc when applicable. + /// + public string LastFailureMessage { get; set; } + + public StatVerbosity Verbosity { get; private set; } + public string ShortName { get; private set; } + public string Name { get; private set; } + public string Description { get; private set; } + + public Check( + string shortName, + string name, + string description, + string category, + string container, + Func checkFunc, + StatVerbosity verbosity) + { + if (ChecksManager.SubCommands.Contains(category)) + throw new Exception( + string.Format("Alert cannot be in category '{0}' since this is reserved for a subcommand", category)); + + foreach (char c in DisallowedShortNameCharacters) + { + if (shortName.IndexOf(c) != -1) + throw new Exception(string.Format("Alert name {0} cannot contain character {1}", shortName, c)); + } + + ShortName = shortName; + Name = name; + Description = description; + Category = category; + Container = container; + CheckFunc = checkFunc; + Verbosity = verbosity; + } + + public bool CheckIt() + { + return CheckFunc(this); + } + + public virtual string ToConsoleString() + { + return string.Format( + "{0}.{1}.{2} - {3}", + Category, + Container, + ShortName, + Description); + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Monitoring/ChecksManager.cs b/OpenSim/Framework/Monitoring/ChecksManager.cs new file mode 100644 index 0000000000..e4a7f8ca2f --- /dev/null +++ b/OpenSim/Framework/Monitoring/ChecksManager.cs @@ -0,0 +1,262 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using log4net; + +namespace OpenSim.Framework.Monitoring +{ + /// + /// Static class used to register/deregister checks on runtime conditions. + /// + public static class ChecksManager + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + // Subcommand used to list other stats. + public const string ListSubCommand = "list"; + + // All subcommands + public static HashSet SubCommands = new HashSet { ListSubCommand }; + + /// + /// Checks categorized by category/container/shortname + /// + /// + /// Do not add or remove directly from this dictionary. + /// + public static SortedDictionary>> RegisteredChecks + = new SortedDictionary>>(); + + public static void RegisterConsoleCommands(ICommandConsole console) + { + console.Commands.AddCommand( + "General", + false, + "show checks", + "show checks", + "Show checks configured for this server", + "If no argument is specified then info on all checks will be shown.\n" + + "'list' argument will show check categories.\n" + + "THIS FACILITY IS EXPERIMENTAL", + HandleShowchecksCommand); + } + + public static void HandleShowchecksCommand(string module, string[] cmd) + { + ICommandConsole con = MainConsole.Instance; + + if (cmd.Length > 2) + { + foreach (string name in cmd.Skip(2)) + { + string[] components = name.Split('.'); + + string categoryName = components[0]; +// string containerName = components.Length > 1 ? components[1] : null; + + if (categoryName == ListSubCommand) + { + con.Output("check categories available are:"); + + foreach (string category in RegisteredChecks.Keys) + con.OutputFormat(" {0}", category); + } +// else +// { +// SortedDictionary> category; +// if (!Registeredchecks.TryGetValue(categoryName, out category)) +// { +// con.OutputFormat("No such category as {0}", categoryName); +// } +// else +// { +// if (String.IsNullOrEmpty(containerName)) +// { +// OutputConfiguredToConsole(con, category); +// } +// else +// { +// SortedDictionary container; +// if (category.TryGetValue(containerName, out container)) +// { +// OutputContainerChecksToConsole(con, container); +// } +// else +// { +// con.OutputFormat("No such container {0} in category {1}", containerName, categoryName); +// } +// } +// } +// } + } + } + else + { + OutputAllChecksToConsole(con); + } + } + + /// + /// Registers a statistic. + /// + /// + /// + public static bool RegisterCheck(Check check) + { + SortedDictionary> category = null, newCategory; + SortedDictionary container = null, newContainer; + + lock (RegisteredChecks) + { + // Check name is not unique across category/container/shortname key. + // XXX: For now just return false. This is to avoid problems in regression tests where all tests + // in a class are run in the same instance of the VM. + if (TryGetCheckParents(check, out category, out container)) + return false; + + // We take a copy-on-write approach here of replacing dictionaries when keys are added or removed. + // This means that we don't need to lock or copy them on iteration, which will be a much more + // common operation after startup. + if (container != null) + newContainer = new SortedDictionary(container); + else + newContainer = new SortedDictionary(); + + if (category != null) + newCategory = new SortedDictionary>(category); + else + newCategory = new SortedDictionary>(); + + newContainer[check.ShortName] = check; + newCategory[check.Container] = newContainer; + RegisteredChecks[check.Category] = newCategory; + } + + return true; + } + + /// + /// Deregister an check + /// > + /// + /// + public static bool DeregisterCheck(Check check) + { + SortedDictionary> category = null, newCategory; + SortedDictionary container = null, newContainer; + + lock (RegisteredChecks) + { + if (!TryGetCheckParents(check, out category, out container)) + return false; + + newContainer = new SortedDictionary(container); + newContainer.Remove(check.ShortName); + + newCategory = new SortedDictionary>(category); + newCategory.Remove(check.Container); + + newCategory[check.Container] = newContainer; + RegisteredChecks[check.Category] = newCategory; + + return true; + } + } + + public static bool TryGetCheckParents( + Check check, + out SortedDictionary> category, + out SortedDictionary container) + { + category = null; + container = null; + + lock (RegisteredChecks) + { + if (RegisteredChecks.TryGetValue(check.Category, out category)) + { + if (category.TryGetValue(check.Container, out container)) + { + if (container.ContainsKey(check.ShortName)) + return true; + } + } + } + + return false; + } + + public static void CheckChecks() + { + lock (RegisteredChecks) + { + foreach (SortedDictionary> category in RegisteredChecks.Values) + { + foreach (SortedDictionary container in category.Values) + { + foreach (Check check in container.Values) + { + if (!check.CheckIt()) + m_log.WarnFormat( + "[CHECKS MANAGER]: Check {0}.{1}.{2} failed with message {3}", check.Category, check.Container, check.ShortName, check.LastFailureMessage); + } + } + } + } + } + + private static void OutputAllChecksToConsole(ICommandConsole con) + { + foreach (var category in RegisteredChecks.Values) + { + OutputCategoryChecksToConsole(con, category); + } + } + + private static void OutputCategoryChecksToConsole( + ICommandConsole con, SortedDictionary> category) + { + foreach (var container in category.Values) + { + OutputContainerChecksToConsole(con, container); + } + } + + private static void OutputContainerChecksToConsole(ICommandConsole con, SortedDictionary container) + { + foreach (Check check in container.Values) + { + con.Output(check.ToConsoleString()); + } + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Monitoring/MemoryWatchdog.cs b/OpenSim/Framework/Monitoring/MemoryWatchdog.cs index c6010cd092..c47462225b 100644 --- a/OpenSim/Framework/Monitoring/MemoryWatchdog.cs +++ b/OpenSim/Framework/Monitoring/MemoryWatchdog.cs @@ -60,17 +60,17 @@ namespace OpenSim.Framework.Monitoring private static bool m_enabled; /// - /// Last memory churn in bytes per millisecond. + /// Average heap allocation rate in bytes per millisecond. /// - public static double AverageMemoryChurn + public static double AverageHeapAllocationRate { get { if (m_samples.Count > 0) return m_samples.Average(); else return 0; } } /// - /// Average memory churn in bytes per millisecond. + /// Last heap allocation in bytes /// - public static double LastMemoryChurn + public static double LastHeapAllocationRate { get { if (m_samples.Count > 0) return m_samples.Last(); else return 0; } } diff --git a/OpenSim/Framework/Monitoring/Properties/AssemblyInfo.cs b/OpenSim/Framework/Monitoring/Properties/AssemblyInfo.cs index 36678bbe3e..07971bf23e 100644 --- a/OpenSim/Framework/Monitoring/Properties/AssemblyInfo.cs +++ b/OpenSim/Framework/Monitoring/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; // Build Number // Revision // -[assembly: AssemblyVersion("0.7.6.*")] +[assembly: AssemblyVersion("0.8.0.*")] diff --git a/OpenSim/Framework/Monitoring/ServerStatsCollector.cs b/OpenSim/Framework/Monitoring/ServerStatsCollector.cs new file mode 100644 index 0000000000..ac0f0bc7be --- /dev/null +++ b/OpenSim/Framework/Monitoring/ServerStatsCollector.cs @@ -0,0 +1,349 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Net.NetworkInformation; +using System.Text; +using System.Threading; +using log4net; +using Nini.Config; +using OpenMetaverse.StructuredData; +using OpenSim.Framework; + +namespace OpenSim.Framework.Monitoring +{ + public class ServerStatsCollector + { + private readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + private readonly string LogHeader = "[SERVER STATS]"; + + public bool Enabled = false; + private static Dictionary RegisteredStats = new Dictionary(); + + public readonly string CategoryServer = "server"; + + public readonly string ContainerThreadpool = "threadpool"; + public readonly string ContainerProcessor = "processor"; + public readonly string ContainerMemory = "memory"; + public readonly string ContainerNetwork = "network"; + public readonly string ContainerProcess = "process"; + + public string NetworkInterfaceTypes = "Ethernet"; + + readonly int performanceCounterSampleInterval = 500; +// int lastperformanceCounterSampleTime = 0; + + private class PerfCounterControl + { + public PerformanceCounter perfCounter; + public int lastFetch; + public string name; + public PerfCounterControl(PerformanceCounter pPc) + : this(pPc, String.Empty) + { + } + public PerfCounterControl(PerformanceCounter pPc, string pName) + { + perfCounter = pPc; + lastFetch = 0; + name = pName; + } + } + + PerfCounterControl processorPercentPerfCounter = null; + + // IRegionModuleBase.Initialize + public void Initialise(IConfigSource source) + { + IConfig cfg = source.Configs["Monitoring"]; + + if (cfg != null) + Enabled = cfg.GetBoolean("ServerStatsEnabled", true); + + if (Enabled) + { + NetworkInterfaceTypes = cfg.GetString("NetworkInterfaceTypes", "Ethernet"); + } + } + + public void Start() + { + if (RegisteredStats.Count == 0) + RegisterServerStats(); + } + + public void Close() + { + if (RegisteredStats.Count > 0) + { + foreach (Stat stat in RegisteredStats.Values) + { + StatsManager.DeregisterStat(stat); + stat.Dispose(); + } + RegisteredStats.Clear(); + } + } + + private void MakeStat(string pName, string pDesc, string pUnit, string pContainer, Action act) + { + MakeStat(pName, pDesc, pUnit, pContainer, act, MeasuresOfInterest.None); + } + + private void MakeStat(string pName, string pDesc, string pUnit, string pContainer, Action act, MeasuresOfInterest moi) + { + string desc = pDesc; + if (desc == null) + desc = pName; + Stat stat = new Stat(pName, pName, desc, pUnit, CategoryServer, pContainer, StatType.Pull, moi, act, StatVerbosity.Debug); + StatsManager.RegisterStat(stat); + RegisteredStats.Add(pName, stat); + } + + public void RegisterServerStats() + { +// lastperformanceCounterSampleTime = Util.EnvironmentTickCount(); + PerformanceCounter tempPC; + Stat tempStat; + string tempName; + + try + { + tempName = "CPUPercent"; + tempPC = new PerformanceCounter("Processor", "% Processor Time", "_Total"); + processorPercentPerfCounter = new PerfCounterControl(tempPC); + // A long time bug in mono is that CPU percent is reported as CPU percent idle. Windows reports CPU percent busy. + tempStat = new Stat(tempName, tempName, "", "percent", CategoryServer, ContainerProcessor, + StatType.Pull, (s) => { GetNextValue(s, processorPercentPerfCounter, Util.IsWindows() ? 1 : -1); }, + StatVerbosity.Info); + StatsManager.RegisterStat(tempStat); + RegisteredStats.Add(tempName, tempStat); + + MakeStat("TotalProcessorTime", null, "sec", ContainerProcessor, + (s) => { s.Value = Process.GetCurrentProcess().TotalProcessorTime.TotalSeconds; }); + + MakeStat("UserProcessorTime", null, "sec", ContainerProcessor, + (s) => { s.Value = Process.GetCurrentProcess().UserProcessorTime.TotalSeconds; }); + + MakeStat("PrivilegedProcessorTime", null, "sec", ContainerProcessor, + (s) => { s.Value = Process.GetCurrentProcess().PrivilegedProcessorTime.TotalSeconds; }); + + MakeStat("Threads", null, "threads", ContainerProcessor, + (s) => { s.Value = Process.GetCurrentProcess().Threads.Count; }); + } + catch (Exception e) + { + m_log.ErrorFormat("{0} Exception creating 'Process': {1}", LogHeader, e); + } + + MakeStat("BuiltinThreadpoolWorkerThreadsAvailable", null, "threads", ContainerThreadpool, + s => + { + int workerThreads, iocpThreads; + ThreadPool.GetAvailableThreads(out workerThreads, out iocpThreads); + s.Value = workerThreads; + }); + + MakeStat("BuiltinThreadpoolIOCPThreadsAvailable", null, "threads", ContainerThreadpool, + s => + { + int workerThreads, iocpThreads; + ThreadPool.GetAvailableThreads(out workerThreads, out iocpThreads); + s.Value = iocpThreads; + }); + + if (Util.FireAndForgetMethod == FireAndForgetMethod.SmartThreadPool && Util.GetSmartThreadPoolInfo() != null) + { + MakeStat("STPMaxThreads", null, "threads", ContainerThreadpool, s => s.Value = Util.GetSmartThreadPoolInfo().MaxThreads); + MakeStat("STPMinThreads", null, "threads", ContainerThreadpool, s => s.Value = Util.GetSmartThreadPoolInfo().MinThreads); + MakeStat("STPConcurrency", null, "threads", ContainerThreadpool, s => s.Value = Util.GetSmartThreadPoolInfo().MaxConcurrentWorkItems); + MakeStat("STPActiveThreads", null, "threads", ContainerThreadpool, s => s.Value = Util.GetSmartThreadPoolInfo().ActiveThreads); + MakeStat("STPInUseThreads", null, "threads", ContainerThreadpool, s => s.Value = Util.GetSmartThreadPoolInfo().InUseThreads); + MakeStat("STPWorkItemsWaiting", null, "threads", ContainerThreadpool, s => s.Value = Util.GetSmartThreadPoolInfo().WaitingCallbacks); + } + + MakeStat( + "HTTPRequestsMade", + "Number of outbound HTTP requests made", + "requests", + ContainerNetwork, + s => s.Value = WebUtil.RequestNumber, + MeasuresOfInterest.AverageChangeOverTime); + + try + { + List okInterfaceTypes = new List(NetworkInterfaceTypes.Split(',')); + + IEnumerable nics = NetworkInterface.GetAllNetworkInterfaces(); + foreach (NetworkInterface nic in nics) + { + if (nic.OperationalStatus != OperationalStatus.Up) + continue; + + string nicInterfaceType = nic.NetworkInterfaceType.ToString(); + if (!okInterfaceTypes.Contains(nicInterfaceType)) + { + m_log.DebugFormat("{0} Not including stats for network interface '{1}' of type '{2}'.", + LogHeader, nic.Name, nicInterfaceType); + m_log.DebugFormat("{0} To include, add to comma separated list in [Monitoring]NetworkInterfaceTypes={1}", + LogHeader, NetworkInterfaceTypes); + continue; + } + + if (nic.Supports(NetworkInterfaceComponent.IPv4)) + { + IPv4InterfaceStatistics nicStats = nic.GetIPv4Statistics(); + if (nicStats != null) + { + MakeStat("BytesRcvd/" + nic.Name, nic.Name, "KB", ContainerNetwork, + (s) => { LookupNic(s, (ns) => { return ns.BytesReceived; }, 1024.0); }); + MakeStat("BytesSent/" + nic.Name, nic.Name, "KB", ContainerNetwork, + (s) => { LookupNic(s, (ns) => { return ns.BytesSent; }, 1024.0); }); + MakeStat("TotalBytes/" + nic.Name, nic.Name, "KB", ContainerNetwork, + (s) => { LookupNic(s, (ns) => { return ns.BytesSent + ns.BytesReceived; }, 1024.0); }); + } + } + // TODO: add IPv6 (it may actually happen someday) + } + } + catch (Exception e) + { + m_log.ErrorFormat("{0} Exception creating 'Network Interface': {1}", LogHeader, e); + } + + MakeStat("ProcessMemory", null, "MB", ContainerMemory, + (s) => { s.Value = Math.Round(Process.GetCurrentProcess().WorkingSet64 / 1024d / 1024d, 3); }); + MakeStat("HeapMemory", null, "MB", ContainerMemory, + (s) => { s.Value = Math.Round(GC.GetTotalMemory(false) / 1024d / 1024d, 3); }); + MakeStat("LastHeapAllocationRate", null, "MB/sec", ContainerMemory, + (s) => { s.Value = Math.Round(MemoryWatchdog.LastHeapAllocationRate * 1000d / 1024d / 1024d, 3); }); + MakeStat("AverageHeapAllocationRate", null, "MB/sec", ContainerMemory, + (s) => { s.Value = Math.Round(MemoryWatchdog.AverageHeapAllocationRate * 1000d / 1024d / 1024d, 3); }); + } + + // Notes on performance counters: + // "How To Read Performance Counters": http://blogs.msdn.com/b/bclteam/archive/2006/06/02/618156.aspx + // "How to get the CPU Usage in C#": http://stackoverflow.com/questions/278071/how-to-get-the-cpu-usage-in-c + // "Mono Performance Counters": http://www.mono-project.com/Mono_Performance_Counters + private delegate double PerfCounterNextValue(); + private void GetNextValue(Stat stat, PerfCounterControl perfControl) + { + GetNextValue(stat, perfControl, 1.0); + } + private void GetNextValue(Stat stat, PerfCounterControl perfControl, double factor) + { + if (Util.EnvironmentTickCountSubtract(perfControl.lastFetch) > performanceCounterSampleInterval) + { + if (perfControl != null && perfControl.perfCounter != null) + { + try + { + // Kludge for factor to run double duty. If -1, subtract the value from one + if (factor == -1) + stat.Value = 1 - perfControl.perfCounter.NextValue(); + else + stat.Value = perfControl.perfCounter.NextValue() / factor; + } + catch (Exception e) + { + m_log.ErrorFormat("{0} Exception on NextValue fetching {1}: {2}", LogHeader, stat.Name, e); + } + perfControl.lastFetch = Util.EnvironmentTickCount(); + } + } + } + + // Lookup the nic that goes with this stat and set the value by using a fetch action. + // Not sure about closure with delegates inside delegates. + private delegate double GetIPv4StatValue(IPv4InterfaceStatistics interfaceStat); + private void LookupNic(Stat stat, GetIPv4StatValue getter, double factor) + { + // Get the one nic that has the name of this stat + IEnumerable nics = NetworkInterface.GetAllNetworkInterfaces().Where( + (network) => network.Name == stat.Description); + try + { + foreach (NetworkInterface nic in nics) + { + IPv4InterfaceStatistics intrStats = nic.GetIPv4Statistics(); + if (intrStats != null) + { + double newVal = Math.Round(getter(intrStats) / factor, 3); + stat.Value = newVal; + } + break; + } + } + catch + { + // There are times interfaces go away so we just won't update the stat for this + m_log.ErrorFormat("{0} Exception fetching stat on interface '{1}'", LogHeader, stat.Description); + } + } + } + + public class ServerStatsAggregator : Stat + { + public ServerStatsAggregator( + string shortName, + string name, + string description, + string unitName, + string category, + string container + ) + : base( + shortName, + name, + description, + unitName, + category, + container, + StatType.Push, + MeasuresOfInterest.None, + null, + StatVerbosity.Info) + { + } + public override string ToConsoleString() + { + StringBuilder sb = new StringBuilder(); + + return sb.ToString(); + } + + public override OSDMap ToOSDMap() + { + OSDMap ret = new OSDMap(); + + return ret; + } + } +} diff --git a/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs b/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs index 6a68322531..f6f458d3ec 100644 --- a/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs +++ b/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs @@ -27,6 +27,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text; using OpenMetaverse; using OpenMetaverse.StructuredData; @@ -39,8 +40,6 @@ namespace OpenSim.Framework.Monitoring /// public class SimExtraStatsCollector : BaseStatsCollector { - private long abnormalClientThreadTerminations; - // private long assetsInCache; // private long texturesInCache; // private long assetCacheMemoryUsage; @@ -73,11 +72,6 @@ namespace OpenSim.Framework.Monitoring private volatile float activeScripts; private volatile float scriptLinesPerSecond; - /// - /// Number of times that a client thread terminated because of an exception - /// - public long AbnormalClientThreadTerminations { get { return abnormalClientThreadTerminations; } } - // /// // /// These statistics are being collected by push rather than pull. Pull would be simpler, but I had the // /// notion of providing some flow statistics (which pull wouldn't give us). Though admittedly these @@ -166,11 +160,6 @@ namespace OpenSim.Framework.Monitoring private IDictionary packetQueueStatsCollectors = new Dictionary(); - public void AddAbnormalClientThreadTermination() - { - abnormalClientThreadTerminations++; - } - // public void AddAsset(AssetBase asset) // { // assetsInCache++; @@ -324,10 +313,12 @@ Asset service request failures: {3}" + Environment.NewLine, sb.Append(Environment.NewLine); sb.Append("CONNECTION STATISTICS"); sb.Append(Environment.NewLine); - sb.Append( - string.Format( - "Abnormal client thread terminations: {0}" + Environment.NewLine, - abnormalClientThreadTerminations)); + + List stats = StatsManager.GetStatsFromEachContainer("clientstack", "ClientLogoutsDueToNoReceives"); + + sb.AppendFormat( + "Client logouts due to no data receive timeout: {0}\n\n", + stats != null ? stats.Sum(s => s.Value).ToString() : "unknown"); // sb.Append(Environment.NewLine); // sb.Append("INVENTORY STATISTICS"); @@ -338,7 +329,7 @@ Asset service request failures: {3}" + Environment.NewLine, // InventoryServiceRetrievalFailures)); sb.Append(Environment.NewLine); - sb.Append("FRAME STATISTICS"); + sb.Append("SAMPLE FRAME STATISTICS"); sb.Append(Environment.NewLine); sb.Append("Dilatn SimFPS PhyFPS AgntUp RootAg ChldAg Prims AtvPrm AtvScr ScrLPS"); sb.Append(Environment.NewLine); diff --git a/OpenSim/Framework/Monitoring/Stats/CounterStat.cs b/OpenSim/Framework/Monitoring/Stats/CounterStat.cs index caea30dd5d..318cf1cb9c 100755 --- a/OpenSim/Framework/Monitoring/Stats/CounterStat.cs +++ b/OpenSim/Framework/Monitoring/Stats/CounterStat.cs @@ -34,142 +34,6 @@ using OpenMetaverse.StructuredData; namespace OpenSim.Framework.Monitoring { -// Create a time histogram of events. The histogram is built in a wrap-around -// array of equally distributed buckets. -// For instance, a minute long histogram of second sized buckets would be: -// new EventHistogram(60, 1000) -public class EventHistogram -{ - private int m_timeBase; - private int m_numBuckets; - private int m_bucketMilliseconds; - private int m_lastBucket; - private int m_totalHistogramMilliseconds; - private long[] m_histogram; - private object histoLock = new object(); - - public EventHistogram(int numberOfBuckets, int millisecondsPerBucket) - { - m_numBuckets = numberOfBuckets; - m_bucketMilliseconds = millisecondsPerBucket; - m_totalHistogramMilliseconds = m_numBuckets * m_bucketMilliseconds; - - m_histogram = new long[m_numBuckets]; - Zero(); - m_lastBucket = 0; - m_timeBase = Util.EnvironmentTickCount(); - } - - public void Event() - { - this.Event(1); - } - - // Record an event at time 'now' in the histogram. - public void Event(int cnt) - { - lock (histoLock) - { - // The time as displaced from the base of the histogram - int bucketTime = Util.EnvironmentTickCountSubtract(m_timeBase); - - // If more than the total time of the histogram, we just start over - if (bucketTime > m_totalHistogramMilliseconds) - { - Zero(); - m_lastBucket = 0; - m_timeBase = Util.EnvironmentTickCount(); - } - else - { - // To which bucket should we add this event? - int bucket = bucketTime / m_bucketMilliseconds; - - // Advance m_lastBucket to the new bucket. Zero any buckets skipped over. - while (bucket != m_lastBucket) - { - // Zero from just after the last bucket to the new bucket or the end - for (int jj = m_lastBucket + 1; jj <= Math.Min(bucket, m_numBuckets - 1); jj++) - { - m_histogram[jj] = 0; - } - m_lastBucket = bucket; - // If the new bucket is off the end, wrap around to the beginning - if (bucket > m_numBuckets) - { - bucket -= m_numBuckets; - m_lastBucket = 0; - m_histogram[m_lastBucket] = 0; - m_timeBase += m_totalHistogramMilliseconds; - } - } - } - m_histogram[m_lastBucket] += cnt; - } - } - - // Get a copy of the current histogram - public long[] GetHistogram() - { - long[] ret = new long[m_numBuckets]; - lock (histoLock) - { - int indx = m_lastBucket + 1; - for (int ii = 0; ii < m_numBuckets; ii++, indx++) - { - if (indx >= m_numBuckets) - indx = 0; - ret[ii] = m_histogram[indx]; - } - } - return ret; - } - - public OSDMap GetHistogramAsOSDMap() - { - OSDMap ret = new OSDMap(); - - ret.Add("Buckets", OSD.FromInteger(m_numBuckets)); - ret.Add("BucketMilliseconds", OSD.FromInteger(m_bucketMilliseconds)); - ret.Add("TotalMilliseconds", OSD.FromInteger(m_totalHistogramMilliseconds)); - - // Compute a number for the first bucket in the histogram. - // This will allow readers to know how this histogram relates to any previously read histogram. - int baseBucketNum = (m_timeBase / m_bucketMilliseconds) + m_lastBucket + 1; - ret.Add("BaseNumber", OSD.FromInteger(baseBucketNum)); - - ret.Add("Values", GetHistogramAsOSDArray()); - - return ret; - } - // Get a copy of the current histogram - public OSDArray GetHistogramAsOSDArray() - { - OSDArray ret = new OSDArray(m_numBuckets); - lock (histoLock) - { - int indx = m_lastBucket + 1; - for (int ii = 0; ii < m_numBuckets; ii++, indx++) - { - if (indx >= m_numBuckets) - indx = 0; - ret[ii] = OSD.FromLong(m_histogram[indx]); - } - } - return ret; - } - - // Zero out the histogram - public void Zero() - { - lock (histoLock) - { - for (int ii = 0; ii < m_numBuckets; ii++) - m_histogram[ii] = 0; - } - } -} - // A statistic that wraps a counter. // Built this way mostly so histograms and history can be created. public class CounterStat : Stat @@ -224,5 +88,32 @@ public class CounterStat : Stat } } } + + // CounterStat is a basic stat plus histograms + public override OSDMap ToOSDMap() + { + // Get the foundational instance + OSDMap map = base.ToOSDMap(); + + map["StatType"] = "CounterStat"; + + // If there are any histograms, add a new field that is an array of histograms as OSDMaps + if (m_histograms.Count > 0) + { + lock (counterLock) + { + if (m_histograms.Count > 0) + { + OSDArray histos = new OSDArray(); + foreach (EventHistogram histo in m_histograms.Values) + { + histos.Add(histo.GetHistogramAsOSDMap()); + } + map.Add("Histograms", histos); + } + } + } + return map; + } } } diff --git a/OpenSim/Framework/Monitoring/Stats/EventHistogram.cs b/OpenSim/Framework/Monitoring/Stats/EventHistogram.cs new file mode 100755 index 0000000000..f51f32247e --- /dev/null +++ b/OpenSim/Framework/Monitoring/Stats/EventHistogram.cs @@ -0,0 +1,173 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using OpenMetaverse.StructuredData; + +namespace OpenSim.Framework.Monitoring +{ +// Create a time histogram of events. The histogram is built in a wrap-around +// array of equally distributed buckets. +// For instance, a minute long histogram of second sized buckets would be: +// new EventHistogram(60, 1000) +public class EventHistogram +{ + private int m_timeBase; + private int m_numBuckets; + private int m_bucketMilliseconds; + private int m_lastBucket; + private int m_totalHistogramMilliseconds; + private long[] m_histogram; + private object histoLock = new object(); + + public EventHistogram(int numberOfBuckets, int millisecondsPerBucket) + { + m_numBuckets = numberOfBuckets; + m_bucketMilliseconds = millisecondsPerBucket; + m_totalHistogramMilliseconds = m_numBuckets * m_bucketMilliseconds; + + m_histogram = new long[m_numBuckets]; + Zero(); + m_lastBucket = 0; + m_timeBase = Util.EnvironmentTickCount(); + } + + public void Event() + { + this.Event(1); + } + + // Record an event at time 'now' in the histogram. + public void Event(int cnt) + { + lock (histoLock) + { + // The time as displaced from the base of the histogram + int bucketTime = Util.EnvironmentTickCountSubtract(m_timeBase); + + // If more than the total time of the histogram, we just start over + if (bucketTime > m_totalHistogramMilliseconds) + { + Zero(); + m_lastBucket = 0; + m_timeBase = Util.EnvironmentTickCount(); + } + else + { + // To which bucket should we add this event? + int bucket = bucketTime / m_bucketMilliseconds; + + // Advance m_lastBucket to the new bucket. Zero any buckets skipped over. + while (bucket != m_lastBucket) + { + // Zero from just after the last bucket to the new bucket or the end + for (int jj = m_lastBucket + 1; jj <= Math.Min(bucket, m_numBuckets - 1); jj++) + { + m_histogram[jj] = 0; + } + m_lastBucket = bucket; + // If the new bucket is off the end, wrap around to the beginning + if (bucket > m_numBuckets) + { + bucket -= m_numBuckets; + m_lastBucket = 0; + m_histogram[m_lastBucket] = 0; + m_timeBase += m_totalHistogramMilliseconds; + } + } + } + m_histogram[m_lastBucket] += cnt; + } + } + + // Get a copy of the current histogram + public long[] GetHistogram() + { + long[] ret = new long[m_numBuckets]; + lock (histoLock) + { + int indx = m_lastBucket + 1; + for (int ii = 0; ii < m_numBuckets; ii++, indx++) + { + if (indx >= m_numBuckets) + indx = 0; + ret[ii] = m_histogram[indx]; + } + } + return ret; + } + + public OSDMap GetHistogramAsOSDMap() + { + OSDMap ret = new OSDMap(); + + ret.Add("Buckets", OSD.FromInteger(m_numBuckets)); + ret.Add("BucketMilliseconds", OSD.FromInteger(m_bucketMilliseconds)); + ret.Add("TotalMilliseconds", OSD.FromInteger(m_totalHistogramMilliseconds)); + + // Compute a number for the first bucket in the histogram. + // This will allow readers to know how this histogram relates to any previously read histogram. + int baseBucketNum = (m_timeBase / m_bucketMilliseconds) + m_lastBucket + 1; + ret.Add("BaseNumber", OSD.FromInteger(baseBucketNum)); + + ret.Add("Values", GetHistogramAsOSDArray()); + + return ret; + } + // Get a copy of the current histogram + public OSDArray GetHistogramAsOSDArray() + { + OSDArray ret = new OSDArray(m_numBuckets); + lock (histoLock) + { + int indx = m_lastBucket + 1; + for (int ii = 0; ii < m_numBuckets; ii++, indx++) + { + if (indx >= m_numBuckets) + indx = 0; + ret[ii] = OSD.FromLong(m_histogram[indx]); + } + } + return ret; + } + + // Zero out the histogram + public void Zero() + { + lock (histoLock) + { + for (int ii = 0; ii < m_numBuckets; ii++) + m_histogram[ii] = 0; + } + } +} + +} diff --git a/OpenSim/Framework/Monitoring/Stats/PercentageStat.cs b/OpenSim/Framework/Monitoring/Stats/PercentageStat.cs index 60bed55500..55ddf0681c 100644 --- a/OpenSim/Framework/Monitoring/Stats/PercentageStat.cs +++ b/OpenSim/Framework/Monitoring/Stats/PercentageStat.cs @@ -29,6 +29,8 @@ using System; using System.Collections.Generic; using System.Text; +using OpenMetaverse.StructuredData; + namespace OpenSim.Framework.Monitoring { public class PercentageStat : Stat @@ -84,5 +86,19 @@ namespace OpenSim.Framework.Monitoring return sb.ToString(); } + + // PercentageStat is a basic stat plus percent calc + public override OSDMap ToOSDMap() + { + // Get the foundational instance + OSDMap map = base.ToOSDMap(); + + map["StatType"] = "PercentageStat"; + + map.Add("Antecedent", OSD.FromLong(Antecedent)); + map.Add("Consequent", OSD.FromLong(Consequent)); + + return map; + } } } \ No newline at end of file diff --git a/OpenSim/Framework/Monitoring/Stats/Stat.cs b/OpenSim/Framework/Monitoring/Stats/Stat.cs index 2e7665f5a1..e095801f2a 100644 --- a/OpenSim/Framework/Monitoring/Stats/Stat.cs +++ b/OpenSim/Framework/Monitoring/Stats/Stat.cs @@ -27,8 +27,10 @@ using System; using System.Collections.Generic; +using System.Linq; +using System.Reflection; using System.Text; - +using log4net; using OpenMetaverse.StructuredData; namespace OpenSim.Framework.Monitoring @@ -38,6 +40,10 @@ namespace OpenSim.Framework.Monitoring /// public class Stat : IDisposable { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public static readonly char[] DisallowedShortNameCharacters = { '.' }; + /// /// Category of this stat (e.g. cache, scene, etc). /// @@ -95,7 +101,7 @@ namespace OpenSim.Framework.Monitoring /// /// Will be null if no measures of interest require samples. /// - private static Queue m_samples; + private Queue m_samples; /// /// Maximum number of statistical samples. @@ -162,6 +168,12 @@ namespace OpenSim.Framework.Monitoring throw new Exception( string.Format("Stat cannot be in category '{0}' since this is reserved for a subcommand", category)); + foreach (char c in DisallowedShortNameCharacters) + { + if (shortName.IndexOf(c) != -1) + throw new Exception(string.Format("Stat name {0} cannot contain character {1}", shortName, c)); + } + ShortName = shortName; Name = name; Description = description; @@ -204,6 +216,8 @@ namespace OpenSim.Framework.Monitoring if (m_samples.Count >= m_maxSamples) m_samples.Dequeue(); +// m_log.DebugFormat("[STAT]: Recording value {0} for {1}", newValue, Name); + m_samples.Enqueue(newValue); } } @@ -211,7 +225,13 @@ namespace OpenSim.Framework.Monitoring public virtual string ToConsoleString() { StringBuilder sb = new StringBuilder(); - sb.AppendFormat("{0}.{1}.{2} : {3} {4}", Category, Container, ShortName, Value, UnitName); + sb.AppendFormat( + "{0}.{1}.{2} : {3}{4}", + Category, + Container, + ShortName, + Value, + string.IsNullOrEmpty(UnitName) ? "" : string.Format(" {0}", UnitName)); AppendMeasuresOfInterest(sb); @@ -221,6 +241,8 @@ namespace OpenSim.Framework.Monitoring public virtual OSDMap ToOSDMap() { OSDMap ret = new OSDMap(); + ret.Add("StatType", "Stat"); // used by overloading classes to denote type of stat + ret.Add("Category", OSD.FromString(Category)); ret.Add("Container", OSD.FromString(Container)); ret.Add("ShortName", OSD.FromString(ShortName)); @@ -229,31 +251,74 @@ namespace OpenSim.Framework.Monitoring ret.Add("UnitName", OSD.FromString(UnitName)); ret.Add("Value", OSD.FromReal(Value)); + double lastChangeOverTime, averageChangeOverTime; + if (ComputeMeasuresOfInterest(out lastChangeOverTime, out averageChangeOverTime)) + { + ret.Add("LastChangeOverTime", OSD.FromReal(lastChangeOverTime)); + ret.Add("AverageChangeOverTime", OSD.FromReal(averageChangeOverTime)); + } + return ret; } - protected void AppendMeasuresOfInterest(StringBuilder sb) + // Compute the averages over time and return same. + // Return 'true' if averages were actually computed. 'false' if no average info. + public bool ComputeMeasuresOfInterest(out double lastChangeOverTime, out double averageChangeOverTime) { - if ((MeasuresOfInterest & MeasuresOfInterest.AverageChangeOverTime) - == MeasuresOfInterest.AverageChangeOverTime) + bool ret = false; + lastChangeOverTime = 0; + averageChangeOverTime = 0; + + if ((MeasuresOfInterest & MeasuresOfInterest.AverageChangeOverTime) == MeasuresOfInterest.AverageChangeOverTime) { double totalChange = 0; + double? penultimateSample = null; double? lastSample = null; lock (m_samples) { + // m_log.DebugFormat( + // "[STAT]: Samples for {0} are {1}", + // Name, string.Join(",", m_samples.Select(s => s.ToString()).ToArray())); + foreach (double s in m_samples) { if (lastSample != null) totalChange += s - (double)lastSample; + penultimateSample = lastSample; lastSample = s; } } + if (lastSample != null && penultimateSample != null) + { + lastChangeOverTime + = ((double)lastSample - (double)penultimateSample) / (Watchdog.WATCHDOG_INTERVAL_MS / 1000); + } + int divisor = m_samples.Count <= 1 ? 1 : m_samples.Count - 1; - sb.AppendFormat(", {0:0.##}{1}/s", totalChange / divisor / (Watchdog.WATCHDOG_INTERVAL_MS / 1000), UnitName); + averageChangeOverTime = totalChange / divisor / (Watchdog.WATCHDOG_INTERVAL_MS / 1000); + ret = true; + } + + return ret; + } + + protected void AppendMeasuresOfInterest(StringBuilder sb) + { + double lastChangeOverTime = 0; + double averageChangeOverTime = 0; + + if (ComputeMeasuresOfInterest(out lastChangeOverTime, out averageChangeOverTime)) + { + sb.AppendFormat( + ", {0:0.##}{1}/s, {2:0.##}{3}/s", + lastChangeOverTime, + string.IsNullOrEmpty(UnitName) ? "" : string.Format(" {0}", UnitName), + averageChangeOverTime, + string.IsNullOrEmpty(UnitName) ? "" : string.Format(" {0}", UnitName)); } } } diff --git a/OpenSim/Framework/Monitoring/StatsLogger.cs b/OpenSim/Framework/Monitoring/StatsLogger.cs new file mode 100644 index 0000000000..1e4fa1175b --- /dev/null +++ b/OpenSim/Framework/Monitoring/StatsLogger.cs @@ -0,0 +1,108 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Reflection; +using System.Timers; +using log4net; + +namespace OpenSim.Framework.Monitoring +{ + /// + /// Provides a means to continuously log stats for debugging purposes. + /// + public static class StatsLogger + { + private static readonly ILog m_statsLog = LogManager.GetLogger("special.StatsLogger"); + + private static Timer m_loggingTimer; + private static int m_statsLogIntervalMs = 5000; + + public static void RegisterConsoleCommands(ICommandConsole console) + { + console.Commands.AddCommand( + "Debug", + false, + "debug stats record", + "debug stats record start|stop", + "Control whether stats are being regularly recorded to a separate file.", + "For debug purposes. Experimental.", + HandleStatsRecordCommand); + } + + public static void HandleStatsRecordCommand(string module, string[] cmd) + { + ICommandConsole con = MainConsole.Instance; + + if (cmd.Length != 4) + { + con.Output("Usage: debug stats record start|stop"); + return; + } + + if (cmd[3] == "start") + { + Start(); + con.OutputFormat("Now recording all stats to file every {0}ms", m_statsLogIntervalMs); + } + else if (cmd[3] == "stop") + { + Stop(); + con.Output("Stopped recording stats to file."); + } + } + + public static void Start() + { + if (m_loggingTimer != null) + Stop(); + + m_loggingTimer = new Timer(m_statsLogIntervalMs); + m_loggingTimer.AutoReset = false; + m_loggingTimer.Elapsed += Log; + m_loggingTimer.Start(); + } + + public static void Stop() + { + if (m_loggingTimer != null) + { + m_loggingTimer.Stop(); + } + } + + private static void Log(object sender, ElapsedEventArgs e) + { + m_statsLog.InfoFormat("*** STATS REPORT AT {0} ***", DateTime.Now); + + foreach (string report in StatsManager.GetAllStatsReports()) + m_statsLog.Info(report); + + m_loggingTimer.Start(); + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Monitoring/StatsManager.cs b/OpenSim/Framework/Monitoring/StatsManager.cs index 24db6d44c0..05ee4c5628 100644 --- a/OpenSim/Framework/Monitoring/StatsManager.cs +++ b/OpenSim/Framework/Monitoring/StatsManager.cs @@ -26,15 +26,20 @@ */ using System; +using System.Collections; using System.Collections.Generic; +using System.Linq; using System.Text; +using OpenSim.Framework; +using OpenMetaverse.StructuredData; + namespace OpenSim.Framework.Monitoring { /// - /// Singleton used to provide access to statistics reporters + /// Static class used to register/deregister/fetch statistics /// - public class StatsManager + public static class StatsManager { // Subcommand used to list other stats. public const string AllSubCommand = "all"; @@ -54,13 +59,13 @@ namespace OpenSim.Framework.Monitoring public static SortedDictionary>> RegisteredStats = new SortedDictionary>>(); - private static AssetStatsCollector assetStats; - private static UserStatsCollector userStats; - private static SimExtraStatsCollector simExtraStats = new SimExtraStatsCollector(); +// private static AssetStatsCollector assetStats; +// private static UserStatsCollector userStats; +// private static SimExtraStatsCollector simExtraStats = new SimExtraStatsCollector(); - public static AssetStatsCollector AssetStats { get { return assetStats; } } - public static UserStatsCollector UserStats { get { return userStats; } } - public static SimExtraStatsCollector SimExtraStats { get { return simExtraStats; } } +// public static AssetStatsCollector AssetStats { get { return assetStats; } } +// public static UserStatsCollector UserStats { get { return userStats; } } + public static SimExtraStatsCollector SimExtraStats { get; set; } public static void RegisterConsoleCommands(ICommandConsole console) { @@ -68,14 +73,18 @@ namespace OpenSim.Framework.Monitoring "General", false, "show stats", - "show stats [list|all|]", + "show stats [list|all|([.])+", "Show statistical information for this server", "If no final argument is specified then legacy statistics information is currently shown.\n" - + "If list is specified then statistic categories are shown.\n" - + "If all is specified then all registered statistics are shown.\n" - + "If a category name is specified then only statistics from that category are shown.\n" + + "'list' argument will show statistic categories.\n" + + "'all' will show all statistics.\n" + + "A name will show statistics from that category.\n" + + "A . name will show statistics from that category in that container.\n" + + "More than one name can be given separated by spaces.\n" + "THIS STATS FACILITY IS EXPERIMENTAL AND DOES NOT YET CONTAIN ALL STATS", HandleShowStatsCommand); + + StatsLogger.RegisterConsoleCommands(console); } public static void HandleShowStatsCommand(string module, string[] cmd) @@ -84,43 +93,47 @@ namespace OpenSim.Framework.Monitoring if (cmd.Length > 2) { - var categoryName = cmd[2]; - var containerName = cmd.Length > 3 ? cmd[3] : String.Empty; + foreach (string name in cmd.Skip(2)) + { + string[] components = name.Split('.'); - if (categoryName == AllSubCommand) - { - foreach (var category in RegisteredStats.Values) + string categoryName = components[0]; + string containerName = components.Length > 1 ? components[1] : null; + + if (categoryName == AllSubCommand) { - OutputCategoryStatsToConsole(con, category); + OutputAllStatsToConsole(con); } - } - else if (categoryName == ListSubCommand) - { - con.Output("Statistic categories available are:"); - foreach (string category in RegisteredStats.Keys) - con.OutputFormat(" {0}", category); - } - else - { - SortedDictionary> category; - if (!RegisteredStats.TryGetValue(categoryName, out category)) + else if (categoryName == ListSubCommand) { - con.OutputFormat("No such category as {0}", categoryName); + con.Output("Statistic categories available are:"); + foreach (string category in RegisteredStats.Keys) + con.OutputFormat(" {0}", category); } else { - if (String.IsNullOrEmpty(containerName)) - OutputCategoryStatsToConsole(con, category); + SortedDictionary> category; + if (!RegisteredStats.TryGetValue(categoryName, out category)) + { + con.OutputFormat("No such category as {0}", categoryName); + } else { - SortedDictionary container; - if (category.TryGetValue(containerName, out container)) + if (String.IsNullOrEmpty(containerName)) { - OutputContainerStatsToConsole(con, container); + OutputCategoryStatsToConsole(con, category); } else { - con.OutputFormat("No such container {0} in category {1}", containerName, categoryName); + SortedDictionary container; + if (category.TryGetValue(containerName, out container)) + { + OutputContainerStatsToConsole(con, container); + } + else + { + con.OutputFormat("No such container {0} in category {1}", containerName, categoryName); + } } } } @@ -129,51 +142,187 @@ namespace OpenSim.Framework.Monitoring else { // Legacy - con.Output(SimExtraStats.Report()); + if (SimExtraStats != null) + con.Output(SimExtraStats.Report()); + else + OutputAllStatsToConsole(con); } } + public static List GetAllStatsReports() + { + List reports = new List(); + + foreach (var category in RegisteredStats.Values) + reports.AddRange(GetCategoryStatsReports(category)); + + return reports; + } + + private static void OutputAllStatsToConsole(ICommandConsole con) + { + foreach (string report in GetAllStatsReports()) + con.Output(report); + } + + private static List GetCategoryStatsReports( + SortedDictionary> category) + { + List reports = new List(); + + foreach (var container in category.Values) + reports.AddRange(GetContainerStatsReports(container)); + + return reports; + } + private static void OutputCategoryStatsToConsole( ICommandConsole con, SortedDictionary> category) { - foreach (var container in category.Values) - { - OutputContainerStatsToConsole(con, container); - } + foreach (string report in GetCategoryStatsReports(category)) + con.Output(report); } - private static void OutputContainerStatsToConsole( ICommandConsole con, SortedDictionary container) + private static List GetContainerStatsReports(SortedDictionary container) { + List reports = new List(); + foreach (Stat stat in container.Values) + reports.Add(stat.ToConsoleString()); + + return reports; + } + + private static void OutputContainerStatsToConsole( + ICommandConsole con, SortedDictionary container) + { + foreach (string report in GetContainerStatsReports(container)) + con.Output(report); + } + + // Creates an OSDMap of the format: + // { categoryName: { + // containerName: { + // statName: { + // "Name": name, + // "ShortName": shortName, + // ... + // }, + // statName: { + // "Name": name, + // "ShortName": shortName, + // ... + // }, + // ... + // }, + // containerName: { + // ... + // }, + // ... + // }, + // categoryName: { + // ... + // }, + // ... + // } + // The passed in parameters will filter the categories, containers and stats returned. If any of the + // parameters are either EmptyOrNull or the AllSubCommand value, all of that type will be returned. + // Case matters. + public static OSDMap GetStatsAsOSDMap(string pCategoryName, string pContainerName, string pStatName) + { + OSDMap map = new OSDMap(); + + foreach (string catName in RegisteredStats.Keys) { - con.Output(stat.ToConsoleString()); + // Do this category if null spec, "all" subcommand or category name matches passed parameter. + // Skip category if none of the above. + if (!(String.IsNullOrEmpty(pCategoryName) || pCategoryName == AllSubCommand || pCategoryName == catName)) + continue; + + OSDMap contMap = new OSDMap(); + foreach (string contName in RegisteredStats[catName].Keys) + { + if (!(string.IsNullOrEmpty(pContainerName) || pContainerName == AllSubCommand || pContainerName == contName)) + continue; + + OSDMap statMap = new OSDMap(); + + SortedDictionary theStats = RegisteredStats[catName][contName]; + foreach (string statName in theStats.Keys) + { + if (!(String.IsNullOrEmpty(pStatName) || pStatName == AllSubCommand || pStatName == statName)) + continue; + + statMap.Add(statName, theStats[statName].ToOSDMap()); + } + + contMap.Add(contName, statMap); + } + map.Add(catName, contMap); } + + return map; } - /// - /// Start collecting statistics related to assets. - /// Should only be called once. - /// - public static AssetStatsCollector StartCollectingAssetStats() + public static Hashtable HandleStatsRequest(Hashtable request) { - assetStats = new AssetStatsCollector(); + Hashtable responsedata = new Hashtable(); +// string regpath = request["uri"].ToString(); + int response_code = 200; + string contenttype = "text/json"; - return assetStats; + string pCategoryName = StatsManager.AllSubCommand; + string pContainerName = StatsManager.AllSubCommand; + string pStatName = StatsManager.AllSubCommand; + + if (request.ContainsKey("cat")) pCategoryName = request["cat"].ToString(); + if (request.ContainsKey("cont")) pContainerName = request["cat"].ToString(); + if (request.ContainsKey("stat")) pStatName = request["cat"].ToString(); + + string strOut = StatsManager.GetStatsAsOSDMap(pCategoryName, pContainerName, pStatName).ToString(); + + // If requestor wants it as a callback function, build response as a function rather than just the JSON string. + if (request.ContainsKey("callback")) + { + strOut = request["callback"].ToString() + "(" + strOut + ");"; + } + + // m_log.DebugFormat("{0} StatFetch: uri={1}, cat={2}, cont={3}, stat={4}, resp={5}", + // LogHeader, regpath, pCategoryName, pContainerName, pStatName, strOut); + + responsedata["int_response_code"] = response_code; + responsedata["content_type"] = contenttype; + responsedata["keepalive"] = false; + responsedata["str_response_string"] = strOut; + responsedata["access_control_allow_origin"] = "*"; + + return responsedata; } - /// - /// Start collecting statistics related to users. - /// Should only be called once. - /// - public static UserStatsCollector StartCollectingUserStats() - { - userStats = new UserStatsCollector(); - - return userStats; - } +// /// +// /// Start collecting statistics related to assets. +// /// Should only be called once. +// /// +// public static AssetStatsCollector StartCollectingAssetStats() +// { +// assetStats = new AssetStatsCollector(); +// +// return assetStats; +// } +// +// /// +// /// Start collecting statistics related to users. +// /// Should only be called once. +// /// +// public static UserStatsCollector StartCollectingUserStats() +// { +// userStats = new UserStatsCollector(); +// +// return userStats; +// } /// - /// Registers a statistic. + /// Register a statistic. /// /// /// @@ -187,7 +336,7 @@ namespace OpenSim.Framework.Monitoring // Stat name is not unique across category/container/shortname key. // XXX: For now just return false. This is to avoid problems in regression tests where all tests // in a class are run in the same instance of the VM. - if (TryGetStat(stat, out category, out container)) + if (TryGetStatParents(stat, out category, out container)) return false; // We take a copy-on-write approach here of replacing dictionaries when keys are added or removed. @@ -223,7 +372,7 @@ namespace OpenSim.Framework.Monitoring lock (RegisteredStats) { - if (!TryGetStat(stat, out category, out container)) + if (!TryGetStatParents(stat, out category, out container)) return false; newContainer = new SortedDictionary(container); @@ -239,12 +388,67 @@ namespace OpenSim.Framework.Monitoring } } - public static bool TryGetStats(string category, out SortedDictionary> stats) + public static bool TryGetStat(string category, string container, string statShortName, out Stat stat) { - return RegisteredStats.TryGetValue(category, out stats); + stat = null; + SortedDictionary> categoryStats; + + lock (RegisteredStats) + { + if (!TryGetStatsForCategory(category, out categoryStats)) + return false; + + SortedDictionary containerStats; + + if (!categoryStats.TryGetValue(container, out containerStats)) + return false; + + return containerStats.TryGetValue(statShortName, out stat); + } } - public static bool TryGetStat( + public static bool TryGetStatsForCategory( + string category, out SortedDictionary> stats) + { + lock (RegisteredStats) + return RegisteredStats.TryGetValue(category, out stats); + } + + /// + /// Get the same stat for each container in a given category. + /// + /// + /// The stats if there were any to fetch. Otherwise null. + /// + /// + /// + public static List GetStatsFromEachContainer(string category, string statShortName) + { + SortedDictionary> categoryStats; + + lock (RegisteredStats) + { + if (!RegisteredStats.TryGetValue(category, out categoryStats)) + return null; + + List stats = null; + + foreach (SortedDictionary containerStats in categoryStats.Values) + { + if (containerStats.ContainsKey(statShortName)) + { + if (stats == null) + stats = new List(); + + stats.Add(containerStats[statShortName]); + } + } + + return stats; + } + } + + public static bool TryGetStatParents( Stat stat, out SortedDictionary> category, out SortedDictionary container) diff --git a/OpenSim/Framework/Monitoring/Watchdog.cs b/OpenSim/Framework/Monitoring/Watchdog.cs index 69d2db58ce..32724ec623 100644 --- a/OpenSim/Framework/Monitoring/Watchdog.cs +++ b/OpenSim/Framework/Monitoring/Watchdog.cs @@ -380,6 +380,7 @@ namespace OpenSim.Framework.Monitoring if (MemoryWatchdog.Enabled) MemoryWatchdog.Update(); + ChecksManager.CheckChecks(); StatsManager.RecordStats(); m_watchdogTimer.Start(); diff --git a/OpenSim/Framework/PermissionsUtil.cs b/OpenSim/Framework/PermissionsUtil.cs new file mode 100644 index 0000000000..d785a781f5 --- /dev/null +++ b/OpenSim/Framework/PermissionsUtil.cs @@ -0,0 +1,87 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using log4net; + +namespace OpenSim.Framework +{ + public static class PermissionsUtil + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Logs permissions flags. Useful when debugging permission problems. + /// + /// + public static void LogPermissions(String name, String message, uint basePerm, uint curPerm, uint nextPerm) + { + m_log.DebugFormat("Permissions of \"{0}\" at \"{1}\": Base {2} ({3:X4}), Current {4} ({5:X4}), NextOwner {6} ({7:X4})", + name, message, + PermissionsToString(basePerm), basePerm, PermissionsToString(curPerm), curPerm, PermissionsToString(nextPerm), nextPerm); + } + + /// + /// Converts a permissions bit-mask to a string (e.g., "MCT"). + /// + private static string PermissionsToString(uint perms) + { + string str = ""; + if ((perms & (int)PermissionMask.Modify) != 0) + str += "M"; + if ((perms & (int)PermissionMask.Copy) != 0) + str += "C"; + if ((perms & (int)PermissionMask.Transfer) != 0) + str += "T"; + if (str == "") + str = "."; + return str; + } + + /// + /// Applies an object's folded permissions to its regular permissions. + /// + /// The folded permissions. Only the lowest 7 bits are examined. + /// The permissions variable to modify. + public static void ApplyFoldedPermissions(uint foldedPerms, ref uint mainPerms) + { + if ((foldedPerms & 7) == 0) + return; // assume that if the folded permissions are 0 then this means that they weren't actually recorded + + if ((foldedPerms & ((uint)PermissionMask.Copy >> 13)) == 0) + mainPerms &= ~(uint)PermissionMask.Copy; + if ((foldedPerms & ((uint)PermissionMask.Transfer >> 13)) == 0) + mainPerms &= ~(uint)PermissionMask.Transfer; + if ((foldedPerms & ((uint)PermissionMask.Modify >> 13)) == 0) + mainPerms &= ~(uint)PermissionMask.Modify; + } + + } +} diff --git a/OpenSim/Framework/PrimitiveBaseShape.cs b/OpenSim/Framework/PrimitiveBaseShape.cs index df928dc226..6a12a45307 100644 --- a/OpenSim/Framework/PrimitiveBaseShape.cs +++ b/OpenSim/Framework/PrimitiveBaseShape.cs @@ -105,6 +105,7 @@ namespace OpenSim.Framework private ushort _profileHollow; private Vector3 _scale; private byte _state; + private byte _lastattach; private ProfileShape _profileShape; private HollowShape _hollowShape; @@ -207,6 +208,7 @@ namespace OpenSim.Framework PCode = (byte)prim.PrimData.PCode; State = prim.PrimData.State; + LastAttachPoint = prim.PrimData.State; PathBegin = Primitive.PackBeginCut(prim.PrimData.PathBegin); PathEnd = Primitive.PackEndCut(prim.PrimData.PathEnd); PathScaleX = Primitive.PackPathScale(prim.PrimData.PathScaleX); @@ -583,6 +585,15 @@ namespace OpenSim.Framework } } + public byte LastAttachPoint { + get { + return _lastattach; + } + set { + _lastattach = value; + } + } + public ProfileShape ProfileShape { get { return _profileShape; diff --git a/OpenSim/Framework/RegionFlags.cs b/OpenSim/Framework/RegionFlags.cs index a3089b077d..7c6569e30c 100644 --- a/OpenSim/Framework/RegionFlags.cs +++ b/OpenSim/Framework/RegionFlags.cs @@ -48,6 +48,7 @@ namespace OpenSim.Framework NoMove = 64, // Don't allow moving this region Reservation = 128, // This is an inactive reservation Authenticate = 256, // Require authentication - Hyperlink = 512 // Record represents a HG link + Hyperlink = 512, // Record represents a HG link + DefaultHGRegion = 1024 // Record represents a default region for hypergrid teleports only. } } \ No newline at end of file diff --git a/OpenSim/Framework/RegionInfo.cs b/OpenSim/Framework/RegionInfo.cs index 24ec181151..ae2ff639eb 100644 --- a/OpenSim/Framework/RegionInfo.cs +++ b/OpenSim/Framework/RegionInfo.cs @@ -100,6 +100,7 @@ namespace OpenSim.Framework public class RegionInfo { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static readonly string LogHeader = "[REGION INFO]"; public bool commFailTF = false; public ConfigurationMember configMember; @@ -139,16 +140,20 @@ namespace OpenSim.Framework public bool m_allow_alternate_ports; protected string m_externalHostName; protected IPEndPoint m_internalEndPoint; - protected uint? m_regionLocX; - protected uint? m_regionLocY; protected uint m_remotingPort; public UUID RegionID = UUID.Zero; public string RemotingAddress; public UUID ScopeID = UUID.Zero; private UUID m_maptileStaticUUID = UUID.Zero; - private Dictionary m_otherSettings = new Dictionary(); + public uint WorldLocX = 0; + public uint WorldLocY = 0; + public uint WorldLocZ = 0; + public uint RegionSizeX = Constants.RegionSize; + public uint RegionSizeY = Constants.RegionSize; + public uint RegionSizeZ = Constants.RegionHeight; + private Dictionary m_otherSettings = new Dictionary(); // Apparently, we're applying the same estatesettings regardless of whether it's local or remote. @@ -233,11 +238,12 @@ namespace OpenSim.Framework m_serverURI = string.Empty; } - public RegionInfo(uint regionLocX, uint regionLocY, IPEndPoint internalEndPoint, string externalUri) + public RegionInfo(uint legacyRegionLocX, uint legacyRegionLocY, IPEndPoint internalEndPoint, string externalUri) { - m_regionLocX = regionLocX; - m_regionLocY = regionLocY; - + RegionLocX = legacyRegionLocX; + RegionLocY = legacyRegionLocY; + RegionSizeX = Constants.RegionSize; + RegionSizeY = Constants.RegionSize; m_internalEndPoint = internalEndPoint; m_externalHostName = externalUri; m_serverURI = string.Empty; @@ -451,25 +457,42 @@ namespace OpenSim.Framework /// /// The x co-ordinate of this region in map tiles (e.g. 1000). + /// Coordinate is scaled as world coordinates divided by the legacy region size + /// and is thus is the number of legacy regions. /// public uint RegionLocX { - get { return m_regionLocX.Value; } - set { m_regionLocX = value; } + get { return WorldLocX / Constants.RegionSize; } + set { WorldLocX = value * Constants.RegionSize; } } /// /// The y co-ordinate of this region in map tiles (e.g. 1000). + /// Coordinate is scaled as world coordinates divided by the legacy region size + /// and is thus is the number of legacy regions. /// public uint RegionLocY { - get { return m_regionLocY.Value; } - set { m_regionLocY = value; } + get { return WorldLocY / Constants.RegionSize; } + set { WorldLocY = value * Constants.RegionSize; } } + public void SetDefaultRegionSize() + { + WorldLocX = 0; + WorldLocY = 0; + WorldLocZ = 0; + RegionSizeX = Constants.RegionSize; + RegionSizeY = Constants.RegionSize; + RegionSizeZ = Constants.RegionHeight; + } + + // A unique region handle is created from the region's world coordinates. + // This cannot be changed because some code expects to receive the region handle and then + // compute the region coordinates from it. public ulong RegionHandle { - get { return Util.UIntsToLong((RegionLocX * (uint) Constants.RegionSize), (RegionLocY * (uint) Constants.RegionSize)); } + get { return Util.UIntsToLong(WorldLocX, WorldLocY); } } public void SetEndPoint(string ipaddr, int port) @@ -576,8 +599,25 @@ namespace OpenSim.Framework string[] locationElements = location.Split(new char[] {','}); - m_regionLocX = Convert.ToUInt32(locationElements[0]); - m_regionLocY = Convert.ToUInt32(locationElements[1]); + RegionLocX = Convert.ToUInt32(locationElements[0]); + RegionLocY = Convert.ToUInt32(locationElements[1]); + + // Region size + // Default to legacy region size if not specified. + allKeys.Remove("SizeX"); + string configSizeX = config.GetString("SizeX", Constants.RegionSize.ToString()); + config.Set("SizeX", configSizeX); + RegionSizeX = Convert.ToUInt32(configSizeX); + allKeys.Remove("SizeY"); + string configSizeY = config.GetString("SizeY", Constants.RegionSize.ToString()); + config.Set("SizeY", configSizeX); + RegionSizeY = Convert.ToUInt32(configSizeY); + allKeys.Remove("SizeZ"); + string configSizeZ = config.GetString("SizeZ", Constants.RegionHeight.ToString()); + config.Set("SizeZ", configSizeX); + RegionSizeZ = Convert.ToUInt32(configSizeZ); + + DoRegionSizeSanityChecks(); // InternalAddress // @@ -697,6 +737,57 @@ namespace OpenSim.Framework } } + // Make sure user specified region sizes are sane. + // Must be multiples of legacy region size (256). + private void DoRegionSizeSanityChecks() + { + if (RegionSizeX != Constants.RegionSize || RegionSizeY != Constants.RegionSize) + { + // Doing non-legacy region sizes. + // Enforce region size to be multiples of the legacy region size (256) + uint partial = RegionSizeX % Constants.RegionSize; + if (partial != 0) + { + RegionSizeX -= partial; + if (RegionSizeX == 0) + RegionSizeX = Constants.RegionSize; + m_log.ErrorFormat("{0} Region size must be multiple of {1}. Enforcing {2}.RegionSizeX={3} instead of specified {4}", + LogHeader, Constants.RegionSize, m_regionName, RegionSizeX, RegionSizeX + partial); + } + partial = RegionSizeY % Constants.RegionSize; + if (partial != 0) + { + RegionSizeY -= partial; + if (RegionSizeY == 0) + RegionSizeY = Constants.RegionSize; + m_log.ErrorFormat("{0} Region size must be multiple of {1}. Enforcing {2}.RegionSizeY={3} instead of specified {4}", + LogHeader, Constants.RegionSize, m_regionName, RegionSizeY, RegionSizeY + partial); + } + + // Because of things in the viewer, regions MUST be square. + // Remove this check when viewers have been updated. + if (RegionSizeX != RegionSizeY) + { + uint minSize = Math.Min(RegionSizeX, RegionSizeY); + RegionSizeX = minSize; + RegionSizeY = minSize; + m_log.ErrorFormat("{0} Regions must be square until viewers are updated. Forcing region {1} size to <{2},{3}>", + LogHeader, m_regionName, RegionSizeX, RegionSizeY); + } + + // There is a practical limit to region size. + if (RegionSizeX > Constants.MaximumRegionSize || RegionSizeY > Constants.MaximumRegionSize) + { + RegionSizeX = Util.Clamp(RegionSizeX, Constants.RegionSize, Constants.MaximumRegionSize); + RegionSizeY = Util.Clamp(RegionSizeY, Constants.RegionSize, Constants.MaximumRegionSize); + m_log.ErrorFormat("{0} Region dimensions must be less than {1}. Clamping {2}'s size to <{3},{4}>", + LogHeader, Constants.MaximumRegionSize, m_regionName, RegionSizeX, RegionSizeY); + } + + m_log.InfoFormat("{0} Region {1} size set to <{2},{3}>", LogHeader, m_regionName, RegionSizeX, RegionSizeY); + } + } + private void WriteNiniConfig(IConfigSource source) { IConfig config = source.Configs[RegionName]; @@ -708,11 +799,17 @@ namespace OpenSim.Framework config.Set("RegionUUID", RegionID.ToString()); - string location = String.Format("{0},{1}", m_regionLocX, m_regionLocY); + string location = String.Format("{0},{1}", RegionLocX, RegionLocY); config.Set("Location", location); if (DataStore != String.Empty) config.Set("Datastore", DataStore); + if (RegionSizeX != Constants.RegionSize || RegionSizeY != Constants.RegionSize) + { + config.Set("SizeX", RegionSizeX); + config.Set("SizeY", RegionSizeY); + config.Set("SizeZ", RegionSizeZ); + } config.Set("InternalAddress", m_internalEndPoint.Address.ToString()); config.Set("InternalPort", m_internalEndPoint.Port); @@ -796,10 +893,18 @@ namespace OpenSim.Framework RegionID.ToString(), true); configMember.addConfigurationOption("sim_name", ConfigurationOption.ConfigurationTypes.TYPE_STRING_NOT_EMPTY, "Region Name", RegionName, true); + configMember.addConfigurationOption("sim_location_x", ConfigurationOption.ConfigurationTypes.TYPE_UINT32, - "Grid Location (X Axis)", m_regionLocX.ToString(), true); + "Grid Location (X Axis)", RegionLocX.ToString(), true); configMember.addConfigurationOption("sim_location_y", ConfigurationOption.ConfigurationTypes.TYPE_UINT32, - "Grid Location (Y Axis)", m_regionLocY.ToString(), true); + "Grid Location (Y Axis)", RegionLocY.ToString(), true); + configMember.addConfigurationOption("sim_size_x", ConfigurationOption.ConfigurationTypes.TYPE_UINT32, + "Size of region in X dimension", RegionSizeX.ToString(), true); + configMember.addConfigurationOption("sim_size_y", ConfigurationOption.ConfigurationTypes.TYPE_UINT32, + "Size of region in Y dimension", RegionSizeY.ToString(), true); + configMember.addConfigurationOption("sim_size_z", ConfigurationOption.ConfigurationTypes.TYPE_UINT32, + "Size of region in Z dimension", RegionSizeZ.ToString(), true); + //m_configMember.addConfigurationOption("datastore", ConfigurationOption.ConfigurationTypes.TYPE_STRING_NOT_EMPTY, "Filename for local storage", "OpenSim.db", false); configMember.addConfigurationOption("internal_ip_address", ConfigurationOption.ConfigurationTypes.TYPE_IP_ADDRESS, @@ -862,10 +967,18 @@ namespace OpenSim.Framework UUID.Random().ToString(), true); configMember.addConfigurationOption("sim_name", ConfigurationOption.ConfigurationTypes.TYPE_STRING_NOT_EMPTY, "Region Name", "OpenSim Test", false); + configMember.addConfigurationOption("sim_location_x", ConfigurationOption.ConfigurationTypes.TYPE_UINT32, "Grid Location (X Axis)", "1000", false); configMember.addConfigurationOption("sim_location_y", ConfigurationOption.ConfigurationTypes.TYPE_UINT32, "Grid Location (Y Axis)", "1000", false); + configMember.addConfigurationOption("sim_size_x", ConfigurationOption.ConfigurationTypes.TYPE_UINT32, + "Size of region in X dimension", Constants.RegionSize.ToString(), false); + configMember.addConfigurationOption("sim_size_y", ConfigurationOption.ConfigurationTypes.TYPE_UINT32, + "Size of region in Y dimension", Constants.RegionSize.ToString(), false); + configMember.addConfigurationOption("sim_size_z", ConfigurationOption.ConfigurationTypes.TYPE_UINT32, + "Size of region in Z dimension", Constants.RegionHeight.ToString(), false); + //m_configMember.addConfigurationOption("datastore", ConfigurationOption.ConfigurationTypes.TYPE_STRING_NOT_EMPTY, "Filename for local storage", "OpenSim.db", false); configMember.addConfigurationOption("internal_ip_address", ConfigurationOption.ConfigurationTypes.TYPE_IP_ADDRESS, @@ -923,10 +1036,19 @@ namespace OpenSim.Framework RegionName = (string) configuration_result; break; case "sim_location_x": - m_regionLocX = (uint) configuration_result; + RegionLocX = (uint) configuration_result; break; case "sim_location_y": - m_regionLocY = (uint) configuration_result; + RegionLocY = (uint) configuration_result; + break; + case "sim_size_x": + RegionSizeX = (uint) configuration_result; + break; + case "sim_size_y": + RegionSizeY = (uint) configuration_result; + break; + case "sim_size_z": + RegionSizeZ = (uint) configuration_result; break; case "datastore": DataStore = (string) configuration_result; @@ -1010,8 +1132,13 @@ namespace OpenSim.Framework args["external_host_name"] = OSD.FromString(ExternalHostName); args["http_port"] = OSD.FromString(HttpPort.ToString()); args["server_uri"] = OSD.FromString(ServerURI); + args["region_xloc"] = OSD.FromString(RegionLocX.ToString()); args["region_yloc"] = OSD.FromString(RegionLocY.ToString()); + args["region_size_x"] = OSD.FromString(RegionSizeX.ToString()); + args["region_size_y"] = OSD.FromString(RegionSizeY.ToString()); + args["region_size_z"] = OSD.FromString(RegionSizeZ.ToString()); + args["internal_ep_address"] = OSD.FromString(InternalEndPoint.Address.ToString()); args["internal_ep_port"] = OSD.FromString(InternalEndPoint.Port.ToString()); if ((RemotingAddress != null) && !RemotingAddress.Equals("")) @@ -1050,6 +1177,13 @@ namespace OpenSim.Framework UInt32.TryParse(args["region_yloc"].AsString(), out locy); RegionLocY = locy; } + if (args.ContainsKey("region_size_x")) + RegionSizeX = (uint)args["region_size_x"].AsInteger(); + if (args.ContainsKey("region_size_y")) + RegionSizeY = (uint)args["region_size_y"].AsInteger(); + if (args.ContainsKey("region_size_z")) + RegionSizeZ = (uint)args["region_size_z"].AsInteger(); + IPAddress ip_addr = null; if (args["internal_ep_address"] != null) { diff --git a/OpenSim/Framework/RegionLoader/Filesystem/Properties/AssemblyInfo.cs b/OpenSim/Framework/RegionLoader/Filesystem/Properties/AssemblyInfo.cs index d4806f17f6..563bcb9f5f 100644 --- a/OpenSim/Framework/RegionLoader/Filesystem/Properties/AssemblyInfo.cs +++ b/OpenSim/Framework/RegionLoader/Filesystem/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; // Build Number // Revision // -[assembly: AssemblyVersion("0.7.6.*")] +[assembly: AssemblyVersion("0.8.0.*")] diff --git a/OpenSim/Framework/RegionLoader/Web/Properties/AssemblyInfo.cs b/OpenSim/Framework/RegionLoader/Web/Properties/AssemblyInfo.cs index 1541a5bced..ab36f108b9 100644 --- a/OpenSim/Framework/RegionLoader/Web/Properties/AssemblyInfo.cs +++ b/OpenSim/Framework/RegionLoader/Web/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; // Build Number // Revision // -[assembly: AssemblyVersion("0.7.6.*")] +[assembly: AssemblyVersion("0.8.0.*")] diff --git a/OpenSim/Framework/RegionSettings.cs b/OpenSim/Framework/RegionSettings.cs index 47a27807b2..dec01ea6be 100644 --- a/OpenSim/Framework/RegionSettings.cs +++ b/OpenSim/Framework/RegionSettings.cs @@ -200,7 +200,7 @@ namespace OpenSim.Framework set { m_ObjectBonus = value; } } - private int m_Maturity = 1; + private int m_Maturity = 0; public int Maturity { @@ -504,21 +504,14 @@ namespace OpenSim.Framework set { m_TelehubEnabled = value; } } - // Connected Telehub object - private UUID m_TelehubObject = UUID.Zero; - public UUID TelehubObject - { - get - { - return m_TelehubObject; - } - set - { - m_TelehubObject = value; - } - } + /// + /// Connected Telehub object + /// + public UUID TelehubObject { get; set; } - // Our Connected Telehub's SpawnPoints + /// + /// Our connected Telehub's SpawnPoints + /// public List l_SpawnPoints = new List(); // Add a SpawnPoint @@ -549,4 +542,4 @@ namespace OpenSim.Framework l_SpawnPoints.Clear(); } } -} \ No newline at end of file +} diff --git a/OpenSim/Framework/SLUtil.cs b/OpenSim/Framework/SLUtil.cs index 537de7ab15..92491050b3 100644 --- a/OpenSim/Framework/SLUtil.cs +++ b/OpenSim/Framework/SLUtil.cs @@ -39,8 +39,32 @@ namespace OpenSim.Framework { // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + /// + /// Asset types used only in OpenSim. + /// To avoid clashing with the code numbers used in Second Life, use only negative numbers here. + /// + public enum OpenSimAssetType : sbyte + { + Material = -2 + } + + #region SL / file extension / content-type conversions + /// + /// Returns the Enum entry corresponding to the given code, regardless of whether it belongs + /// to the AssetType or OpenSimAssetType enums. + /// + public static object AssetTypeFromCode(sbyte assetType) + { + if (Enum.IsDefined(typeof(OpenMetaverse.AssetType), assetType)) + return (OpenMetaverse.AssetType)assetType; + else if (Enum.IsDefined(typeof(OpenSimAssetType), assetType)) + return (OpenSimAssetType)assetType; + else + return OpenMetaverse.AssetType.Unknown; + } + private class TypeMapping { private sbyte assetType; @@ -56,12 +80,7 @@ namespace OpenSim.Framework public object AssetType { - get { - if (Enum.IsDefined(typeof(OpenMetaverse.AssetType), assetType)) - return (OpenMetaverse.AssetType)assetType; - else - return OpenMetaverse.AssetType.Unknown; - } + get { return AssetTypeFromCode(assetType); } } public InventoryType InventoryType @@ -102,6 +121,11 @@ namespace OpenSim.Framework : this((sbyte)assetType, inventoryType, contentType, null, extension) { } + + public TypeMapping(OpenSimAssetType assetType, InventoryType inventoryType, string contentType, string extension) + : this((sbyte)assetType, inventoryType, contentType, null, extension) + { + } } /// @@ -142,7 +166,9 @@ namespace OpenSim.Framework new TypeMapping(AssetType.CurrentOutfitFolder, InventoryType.Unknown, "application/vnd.ll.currentoutfitfolder", "currentoutfitfolder"), new TypeMapping(AssetType.OutfitFolder, InventoryType.Unknown, "application/vnd.ll.outfitfolder", "outfitfolder"), new TypeMapping(AssetType.MyOutfitsFolder, InventoryType.Unknown, "application/vnd.ll.myoutfitsfolder", "myoutfitsfolder"), - new TypeMapping(AssetType.Mesh, InventoryType.Mesh, "application/vnd.ll.mesh", "llm") + new TypeMapping(AssetType.Mesh, InventoryType.Mesh, "application/vnd.ll.mesh", "llm"), + + new TypeMapping(OpenSimAssetType.Material, InventoryType.Unknown, "application/llsd+xml", "material") }; private static Dictionary asset2Content; @@ -247,12 +273,18 @@ namespace OpenSim.Framework /// public static List ParseNotecardToList(string rawInput) { - string[] input = rawInput.Replace("\r", "").Split('\n'); + string[] input; int idx = 0; int level = 0; List output = new List(); string[] words; + //The Linden format always ends with a } after the input data. + //Strip off trailing } so there is nothing after the input data. + int i = rawInput.LastIndexOf("}"); + rawInput = rawInput.Remove(i, rawInput.Length-i); + input = rawInput.Replace("\r", "").Split('\n'); + while (idx < input.Length) { if (input[idx] == "{") @@ -287,24 +319,18 @@ namespace OpenSim.Framework break; if (words[0] == "Text") { - int len = int.Parse(words[2]); - idx++; + idx++; //Now points to first line of notecard text - int count = -1; + //Number of lines in notecard. + int lines = input.Length - idx; + int line = 0; - while (count < len && idx < input.Length) + while (line < lines) { - // int l = input[idx].Length; - string ln = input[idx]; - - int need = len-count-1; - if (ln.Length > need) - ln = ln.Substring(0, need); - -// m_log.DebugFormat("[PARSE NOTECARD]: Adding line {0}", ln); - output.Add(ln); - count += ln.Length + 1; +// m_log.DebugFormat("[PARSE NOTECARD]: Adding line {0}", input[idx]); + output.Add(input[idx]); idx++; + line++; } return output; diff --git a/OpenSim/Framework/Serialization/ArchiveConstants.cs b/OpenSim/Framework/Serialization/ArchiveConstants.cs index 0c12787882..73ebfae234 100644 --- a/OpenSim/Framework/Serialization/ArchiveConstants.cs +++ b/OpenSim/Framework/Serialization/ArchiveConstants.cs @@ -29,6 +29,7 @@ using System; using System.Collections.Generic; using System.Text; using OpenMetaverse; +using OpenSimAssetType = OpenSim.Framework.SLUtil.OpenSimAssetType; namespace OpenSim.Framework.Serialization { @@ -128,6 +129,7 @@ namespace OpenSim.Framework.Serialization ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Texture] = ASSET_EXTENSION_SEPARATOR + "texture.jp2"; ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.TextureTGA] = ASSET_EXTENSION_SEPARATOR + "texture.tga"; ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.TrashFolder] = ASSET_EXTENSION_SEPARATOR + "trashfolder.txt"; // Not sure if we'll ever see this + ASSET_TYPE_TO_EXTENSION[(sbyte)OpenSimAssetType.Material] = ASSET_EXTENSION_SEPARATOR + "material.xml"; // Not sure if we'll ever see this EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "animation.bvh"] = (sbyte)AssetType.Animation; EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "bodypart.txt"] = (sbyte)AssetType.Bodypart; @@ -152,6 +154,7 @@ namespace OpenSim.Framework.Serialization EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "texture.jp2"] = (sbyte)AssetType.Texture; EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "texture.tga"] = (sbyte)AssetType.TextureTGA; EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "trashfolder.txt"] = (sbyte)AssetType.TrashFolder; + EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "material.xml"] = (sbyte)OpenSimAssetType.Material; } public static string CreateOarLandDataPath(LandData ld) diff --git a/OpenSim/Framework/Serialization/External/UserInventoryItemSerializer.cs b/OpenSim/Framework/Serialization/External/UserInventoryItemSerializer.cs index 88f9581afd..f2a6b8b87a 100644 --- a/OpenSim/Framework/Serialization/External/UserInventoryItemSerializer.cs +++ b/OpenSim/Framework/Serialization/External/UserInventoryItemSerializer.cs @@ -277,7 +277,7 @@ namespace OpenSim.Framework.Serialization.External writer.WriteStartElement("GroupOwned"); writer.WriteString(inventoryItem.GroupOwned.ToString()); writer.WriteEndElement(); - if (options.ContainsKey("creators") && inventoryItem.CreatorData != null && inventoryItem.CreatorData != string.Empty) + if (options.ContainsKey("creators") && !string.IsNullOrEmpty(inventoryItem.CreatorData)) writer.WriteElementString("CreatorData", inventoryItem.CreatorData); else if (options.ContainsKey("home")) { diff --git a/OpenSim/Framework/Serialization/Properties/AssemblyInfo.cs b/OpenSim/Framework/Serialization/Properties/AssemblyInfo.cs index a8dff93458..3f041488cb 100644 --- a/OpenSim/Framework/Serialization/Properties/AssemblyInfo.cs +++ b/OpenSim/Framework/Serialization/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; // Build Number // Revision // -[assembly: AssemblyVersion("0.7.6.*")] +[assembly: AssemblyVersion("0.8.0.*")] diff --git a/OpenSim/Framework/Servers/BaseOpenSimServer.cs b/OpenSim/Framework/Servers/BaseOpenSimServer.cs index 6c04c694f8..cbd34a234c 100644 --- a/OpenSim/Framework/Servers/BaseOpenSimServer.cs +++ b/OpenSim/Framework/Servers/BaseOpenSimServer.cs @@ -86,25 +86,22 @@ namespace OpenSim.Framework.Servers /// protected virtual void StartupSpecific() { - if (m_console == null) - return; - + StatsManager.SimExtraStats = new SimExtraStatsCollector(); RegisterCommonCommands(); - - m_console.Commands.AddCommand("General", false, "quit", - "quit", - "Quit the application", HandleQuit); + RegisterCommonComponents(Config); + } - m_console.Commands.AddCommand("General", false, "shutdown", - "shutdown", - "Quit the application", HandleQuit); + protected override void ShutdownSpecific() + { + m_log.Info("[SHUTDOWN]: Shutdown processing on main thread complete. Exiting..."); + + RemovePIDFile(); + + base.ShutdownSpecific(); + + Environment.Exit(0); } - /// - /// Should be overriden and referenced by descendents if they need to perform extra shutdown processing - /// - public virtual void ShutdownSpecific() {} - /// /// Provides a list of help topics that are available. Overriding classes should append their topics to the /// information returned when the base method is called. @@ -148,30 +145,13 @@ namespace OpenSim.Framework.Servers TimeSpan timeTaken = DateTime.Now - m_startuptime; - m_log.InfoFormat( - "[STARTUP]: Non-script portion of startup took {0}m {1}s. PLEASE WAIT FOR LOGINS TO BE ENABLED ON REGIONS ONCE SCRIPTS HAVE STARTED.", + MainConsole.Instance.OutputFormat( + "PLEASE WAIT FOR LOGINS TO BE ENABLED ON REGIONS ONCE SCRIPTS HAVE STARTED. Non-script portion of startup took {0}m {1}s.", timeTaken.Minutes, timeTaken.Seconds); } - /// - /// Should be overriden and referenced by descendents if they need to perform extra shutdown processing - /// - public virtual void Shutdown() + public string osSecret { - ShutdownSpecific(); - - m_log.Info("[SHUTDOWN]: Shutdown processing on main thread complete. Exiting..."); - RemovePIDFile(); - - Environment.Exit(0); - } - - private void HandleQuit(string module, string[] args) - { - Shutdown(); - } - - public string osSecret { // Secret uuid for the simulator get { return m_osSecret; } } diff --git a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs index 97035e385c..7841f472f2 100644 --- a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs +++ b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs @@ -54,7 +54,6 @@ namespace OpenSim.Framework.Servers.HttpServer private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private HttpServerLogWriter httpserverlog = new HttpServerLogWriter(); - /// /// This is a pending websocket request before it got an sucessful upgrade response. /// The consumer must call handler.HandshakeAndUpgrade() to signal to the handler to @@ -81,6 +80,11 @@ namespace OpenSim.Framework.Servers.HttpServer /// public int RequestNumber { get; private set; } + /// + /// Statistic for holding number of requests processed. + /// + private Stat m_requestsProcessedStat; + private volatile int NotSocketErrors = 0; public volatile bool HTTPDRunning = false; @@ -383,6 +387,8 @@ namespace OpenSim.Framework.Servers.HttpServer if (TryGetPollServiceHTTPHandler(request.UriPath.ToString(), out psEvArgs)) { + psEvArgs.RequestsReceived++; + PollServiceHttpRequest psreq = new PollServiceHttpRequest(psEvArgs, context, request); if (psEvArgs.Request != null) @@ -437,9 +443,8 @@ namespace OpenSim.Framework.Servers.HttpServer } } - public void OnHandleRequestIOThread(IHttpClientContext context, IHttpRequest request) + private void OnHandleRequestIOThread(IHttpClientContext context, IHttpRequest request) { - OSHttpRequest req = new OSHttpRequest(context, request); WebSocketRequestDelegate dWebSocketRequestDelegate = null; lock (m_WebSocketHandlers) @@ -454,9 +459,8 @@ namespace OpenSim.Framework.Servers.HttpServer } OSHttpResponse resp = new OSHttpResponse(new HttpResponse(context, request),context); - - HandleRequest(req, resp); - + resp.ReuseContext = true; + HandleRequest(req, resp); // !!!HACK ALERT!!! // There seems to be a bug in the underlying http code that makes subsequent requests @@ -687,7 +691,7 @@ namespace OpenSim.Framework.Servers.HttpServer if (buffer != null) { - if (!response.SendChunked) + if (!response.SendChunked && response.ContentLength64 <= 0) response.ContentLength64 = buffer.LongLength; response.OutputStream.Write(buffer, 0, buffer.Length); @@ -782,7 +786,7 @@ namespace OpenSim.Framework.Servers.HttpServer "[BASE HTTP SERVER]: HTTP IN {0} :{1} {2} content type handler {3} {4} from {5}", RequestNumber, Port, - (request.ContentType == null || request.ContentType == "") ? "not set" : request.ContentType, + string.IsNullOrEmpty(request.ContentType) ? "not set" : request.ContentType, request.HttpMethod, request.Url.PathAndQuery, request.RemoteIPEndPoint); @@ -1053,7 +1057,21 @@ namespace OpenSim.Framework.Servers.HttpServer } response.ContentType = "text/xml"; - responseString = XmlRpcResponseSerializer.Singleton.Serialize(xmlRpcResponse); + using (MemoryStream outs = new MemoryStream()) + { + using (XmlTextWriter writer = new XmlTextWriter(outs, Encoding.UTF8)) + { + writer.Formatting = Formatting.None; + XmlRpcResponseSerializer.Singleton.Serialize(writer, xmlRpcResponse); + writer.Flush(); + outs.Flush(); + outs.Position = 0; + using (StreamReader sr = new StreamReader(outs)) + { + responseString = sr.ReadToEnd(); + } + } + } } else { @@ -1850,8 +1868,8 @@ namespace OpenSim.Framework.Servers.HttpServer m_httpListener2.Start(64); // Long Poll Service Manager with 3 worker threads a 25 second timeout for no events -// m_PollServiceManager = new PollServiceRequestManager(this, 3, 25000); m_PollServiceManager = new PollServiceRequestManager(this, 4, 25000); + m_PollServiceManager.Start(); HTTPDRunning = true; //HttpListenerContext context; @@ -1870,6 +1888,21 @@ namespace OpenSim.Framework.Servers.HttpServer // useful without inbound HTTP. throw e; } + + m_requestsProcessedStat + = new Stat( + "HTTPRequestsServed", + "Number of inbound HTTP requests processed", + "", + "requests", + "httpserver", + Port.ToString(), + StatType.Pull, + MeasuresOfInterest.AverageChangeOverTime, + stat => stat.Value = RequestNumber, + StatVerbosity.Debug); + + StatsManager.RegisterStat(m_requestsProcessedStat); } public void httpServerDisconnectMonitor(IHttpClientContext source, SocketError err) @@ -1902,9 +1935,12 @@ namespace OpenSim.Framework.Servers.HttpServer public void Stop() { HTTPDRunning = false; + + StatsManager.DeregisterStat(m_requestsProcessedStat); + try { -// m_PollServiceManager.Stop(); + m_PollServiceManager.Stop(); m_httpListener2.ExceptionThrown -= httpServerException; //m_httpListener2.DisconnectHandler = null; @@ -1931,6 +1967,7 @@ namespace OpenSim.Framework.Servers.HttpServer public void RemoveHTTPHandler(string httpMethod, string path) { + if (path == null) return; // Caps module isn't loaded, tries to remove handler where path = null lock (m_HTTPHandlers) { if (httpMethod != null && httpMethod.Length == 0) diff --git a/OpenSim/Framework/Servers/HttpServer/BaseOutputStreamHandler.cs b/OpenSim/Framework/Servers/HttpServer/BaseOutputStreamHandler.cs new file mode 100644 index 0000000000..72b3065172 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/BaseOutputStreamHandler.cs @@ -0,0 +1,60 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System.IO; + +namespace OpenSim.Framework.Servers.HttpServer +{ + /// + /// Base handler for writing to an output stream + /// + /// + /// Inheriting classes should override ProcessRequest() rather than Handle() + /// + public abstract class BaseOutputStreamHandler : BaseRequestHandler, IRequestHandler + { + protected BaseOutputStreamHandler(string httpMethod, string path) : this(httpMethod, path, null, null) {} + + protected BaseOutputStreamHandler(string httpMethod, string path, string name, string description) + : base(httpMethod, path, name, description) {} + + public virtual void Handle( + string path, Stream request, Stream response, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + RequestsReceived++; + + ProcessRequest(path, request, response, httpRequest, httpResponse); + + RequestsHandled++; + } + + protected virtual void ProcessRequest( + string path, Stream request, Stream response, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Servers/HttpServer/BaseRequestHandler.cs b/OpenSim/Framework/Servers/HttpServer/BaseRequestHandler.cs index ae7aaf2e3e..bbac699d3e 100644 --- a/OpenSim/Framework/Servers/HttpServer/BaseRequestHandler.cs +++ b/OpenSim/Framework/Servers/HttpServer/BaseRequestHandler.cs @@ -31,6 +31,10 @@ namespace OpenSim.Framework.Servers.HttpServer { public abstract class BaseRequestHandler { + public int RequestsReceived { get; protected set; } + + public int RequestsHandled { get; protected set; } + public virtual string ContentType { get { return "application/xml"; } diff --git a/OpenSim/Framework/Servers/HttpServer/BaseStreamHandler.cs b/OpenSim/Framework/Servers/HttpServer/BaseStreamHandler.cs index 6342983a55..252cc2ad81 100644 --- a/OpenSim/Framework/Servers/HttpServer/BaseStreamHandler.cs +++ b/OpenSim/Framework/Servers/HttpServer/BaseStreamHandler.cs @@ -29,14 +29,35 @@ using System.IO; namespace OpenSim.Framework.Servers.HttpServer { + /// + /// Base streamed request handler. + /// + /// + /// Inheriting classes should override ProcessRequest() rather than Handle() + /// public abstract class BaseStreamHandler : BaseRequestHandler, IStreamedRequestHandler { - public abstract byte[] Handle(string path, Stream request, - IOSHttpRequest httpRequest, IOSHttpResponse httpResponse); - protected BaseStreamHandler(string httpMethod, string path) : this(httpMethod, path, null, null) {} protected BaseStreamHandler(string httpMethod, string path, string name, string description) : base(httpMethod, path, name, description) {} + + public virtual byte[] Handle( + string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + RequestsReceived++; + + byte[] result = ProcessRequest(path, request, httpRequest, httpResponse); + + RequestsHandled++; + + return result; + } + + protected virtual byte[] ProcessRequest( + string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + return null; + } } } \ No newline at end of file diff --git a/OpenSim/Framework/Servers/HttpServer/BaseStreamHandlerBasicDOSProtector.cs b/OpenSim/Framework/Servers/HttpServer/BaseStreamHandlerBasicDOSProtector.cs new file mode 100644 index 0000000000..1b8854573b --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/BaseStreamHandlerBasicDOSProtector.cs @@ -0,0 +1,107 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using OpenSim.Framework; +using System.IO; + +namespace OpenSim.Framework.Servers.HttpServer +{ + /// + /// BaseStreamHandlerBasicDOSProtector Base streamed request handler. + /// + /// + /// Inheriting classes should override ProcessRequest() rather than Handle() + /// + public abstract class BaseStreamHandlerBasicDOSProtector : BaseRequestHandler, IStreamedRequestHandler + { + + private readonly BasicDosProtectorOptions _options; + private readonly BasicDOSProtector _dosProtector; + + protected BaseStreamHandlerBasicDOSProtector(string httpMethod, string path, BasicDosProtectorOptions options) : this(httpMethod, path, null, null, options) {} + + protected BaseStreamHandlerBasicDOSProtector(string httpMethod, string path, string name, string description, BasicDosProtectorOptions options) + : base(httpMethod, path, name, description) + { + _options = options; + _dosProtector = new BasicDOSProtector(_options); + } + + public virtual byte[] Handle( + string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + byte[] result; + RequestsReceived++; + string clientstring = GetClientString(httpRequest); + string endpoint = GetRemoteAddr(httpRequest); + if (_dosProtector.Process(clientstring, endpoint)) + result = ProcessRequest(path, request, httpRequest, httpResponse); + else + result = ThrottledRequest(path, request, httpRequest, httpResponse); + if (_options.MaxConcurrentSessions > 0) + _dosProtector.ProcessEnd(clientstring, endpoint); + + RequestsHandled++; + + return result; + } + + protected virtual byte[] ProcessRequest( + string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + return null; + } + + protected virtual byte[] ThrottledRequest( + string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + return new byte[0]; + } + + + private string GetRemoteAddr(IOSHttpRequest httpRequest) + { + string remoteaddr = string.Empty; + if (httpRequest.Headers["remote_addr"] != null) + remoteaddr = httpRequest.Headers["remote_addr"]; + + return remoteaddr; + } + + private string GetClientString(IOSHttpRequest httpRequest) + { + string clientstring = string.Empty; + + if (_options.AllowXForwardedFor && httpRequest.Headers["x-forwarded-for"] != null) + clientstring = httpRequest.Headers["x-forwarded-for"]; + else + clientstring = GetRemoteAddr(httpRequest); + + return clientstring; + + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/BinaryStreamHandler.cs b/OpenSim/Framework/Servers/HttpServer/BinaryStreamHandler.cs index b94bfb4311..1b03f543d3 100644 --- a/OpenSim/Framework/Servers/HttpServer/BinaryStreamHandler.cs +++ b/OpenSim/Framework/Servers/HttpServer/BinaryStreamHandler.cs @@ -45,7 +45,7 @@ namespace OpenSim.Framework.Servers.HttpServer m_method = binaryMethod; } - public override byte[] Handle(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + protected override byte[] ProcessRequest(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) { byte[] data = ReadFully(request); string param = GetParam(path); diff --git a/OpenSim/Framework/Servers/HttpServer/GenericHTTPBasicDOSProtector.cs b/OpenSim/Framework/Servers/HttpServer/GenericHTTPBasicDOSProtector.cs new file mode 100644 index 0000000000..cd4b8ffa29 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/GenericHTTPBasicDOSProtector.cs @@ -0,0 +1,119 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System.Collections; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public class GenericHTTPDOSProtector + { + private readonly GenericHTTPMethod _normalMethod; + private readonly GenericHTTPMethod _throttledMethod; + + private readonly BasicDosProtectorOptions _options; + private readonly BasicDOSProtector _dosProtector; + + public GenericHTTPDOSProtector(GenericHTTPMethod normalMethod, GenericHTTPMethod throttledMethod, BasicDosProtectorOptions options) + { + _normalMethod = normalMethod; + _throttledMethod = throttledMethod; + + _options = options; + _dosProtector = new BasicDOSProtector(_options); + } + public Hashtable Process(Hashtable request) + { + Hashtable process = null; + string clientstring= GetClientString(request); + string endpoint = GetRemoteAddr(request); + if (_dosProtector.Process(clientstring, endpoint)) + process = _normalMethod(request); + else + process = _throttledMethod(request); + + if (_options.MaxConcurrentSessions>0) + _dosProtector.ProcessEnd(clientstring, endpoint); + + return process; + } + + private string GetRemoteAddr(Hashtable request) + { + string remoteaddr = ""; + if (!request.ContainsKey("headers")) + return remoteaddr; + Hashtable requestinfo = (Hashtable)request["headers"]; + if (!requestinfo.ContainsKey("remote_addr")) + return remoteaddr; + object remote_addrobj = requestinfo["remote_addr"]; + if (remote_addrobj != null) + { + if (!string.IsNullOrEmpty(remote_addrobj.ToString())) + { + remoteaddr = remote_addrobj.ToString(); + } + + } + return remoteaddr; + } + + private string GetClientString(Hashtable request) + { + string clientstring = ""; + if (!request.ContainsKey("headers")) + return clientstring; + + Hashtable requestinfo = (Hashtable)request["headers"]; + if (_options.AllowXForwardedFor && requestinfo.ContainsKey("x-forwarded-for")) + { + object str = requestinfo["x-forwarded-for"]; + if (str != null) + { + if (!string.IsNullOrEmpty(str.ToString())) + { + return str.ToString(); + } + } + } + if (!requestinfo.ContainsKey("remote_addr")) + return clientstring; + + object remote_addrobj = requestinfo["remote_addr"]; + if (remote_addrobj != null) + { + if (!string.IsNullOrEmpty(remote_addrobj.ToString())) + { + clientstring = remote_addrobj.ToString(); + } + } + + return clientstring; + + } + + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/Interfaces/IStreamHandler.cs b/OpenSim/Framework/Servers/HttpServer/Interfaces/IStreamHandler.cs index cb5cce5f79..b8541cbe4c 100644 --- a/OpenSim/Framework/Servers/HttpServer/Interfaces/IStreamHandler.cs +++ b/OpenSim/Framework/Servers/HttpServer/Interfaces/IStreamHandler.cs @@ -32,7 +32,6 @@ namespace OpenSim.Framework.Servers.HttpServer { public interface IRequestHandler { - /// /// Name for this handler. /// @@ -59,6 +58,19 @@ namespace OpenSim.Framework.Servers.HttpServer // Return path string Path { get; } + + /// + /// Number of requests received by this handler + /// + int RequestsReceived { get; } + + /// + /// Number of requests handled. + /// + /// + /// Should be equal to RequestsReceived unless requested are being handled slowly or there is deadlock. + /// + int RequestsHandled { get; } } public interface IStreamedRequestHandler : IRequestHandler @@ -69,7 +81,6 @@ namespace OpenSim.Framework.Servers.HttpServer public interface IStreamHandler : IRequestHandler { - // Handle request stream, return byte array void Handle(string path, Stream request, Stream response, IOSHttpRequest httpReqbuest, IOSHttpResponse httpResponse); } diff --git a/OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs b/OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs index c19ac320da..3fd3bf703c 100644 --- a/OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs +++ b/OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs @@ -50,25 +50,39 @@ namespace OpenSim.Framework.Servers.HttpServer public enum EventType : int { - Normal = 0, + LongPoll = 0, LslHttp = 1, Inventory = 2, Texture = 3, Mesh = 4 } + public string Url { get; set; } + + /// + /// Number of requests received for this poll service. + /// + public int RequestsReceived { get; set; } + + /// + /// Number of requests handled by this poll service. + /// + public int RequestsHandled { get; set; } + public PollServiceEventArgs( RequestMethod pRequest, + string pUrl, HasEventsMethod pHasEvents, GetEventsMethod pGetEvents, NoEventsMethod pNoEvents, UUID pId, int pTimeOutms) { Request = pRequest; + Url = pUrl; HasEvents = pHasEvents; GetEvents = pGetEvents; NoEvents = pNoEvents; Id = pId; TimeOutms = pTimeOutms; - Type = EventType.Normal; + Type = EventType.LongPoll; } } } diff --git a/OpenSim/Framework/Servers/HttpServer/PollServiceHttpRequest.cs b/OpenSim/Framework/Servers/HttpServer/PollServiceHttpRequest.cs index 723530ae15..6aa94794ee 100644 --- a/OpenSim/Framework/Servers/HttpServer/PollServiceHttpRequest.cs +++ b/OpenSim/Framework/Servers/HttpServer/PollServiceHttpRequest.cs @@ -26,13 +26,19 @@ */ using System; +using System.Collections; +using System.Reflection; +using System.Text; using HttpServer; +using log4net; using OpenMetaverse; namespace OpenSim.Framework.Servers.HttpServer { public class PollServiceHttpRequest { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + public readonly PollServiceEventArgs PollServiceArgs; public readonly IHttpClientContext HttpContext; public readonly IHttpRequest Request; @@ -48,5 +54,44 @@ namespace OpenSim.Framework.Servers.HttpServer RequestTime = System.Environment.TickCount; RequestID = UUID.Random(); } + + internal void DoHTTPGruntWork(BaseHttpServer server, Hashtable responsedata) + { + OSHttpResponse response + = new OSHttpResponse(new HttpResponse(HttpContext, Request), HttpContext); + + byte[] buffer = server.DoHTTPGruntWork(responsedata, response); + + response.SendChunked = false; + response.ContentLength64 = buffer.Length; + response.ContentEncoding = Encoding.UTF8; + + try + { + response.OutputStream.Write(buffer, 0, buffer.Length); + } + catch (Exception ex) + { + m_log.Warn(string.Format("[POLL SERVICE WORKER THREAD]: Error ", ex)); + } + finally + { + //response.OutputStream.Close(); + try + { + response.OutputStream.Flush(); + response.Send(); + + //if (!response.KeepAlive && response.ReuseContext) + // response.FreeContext(); + } + catch (Exception e) + { + m_log.Warn(String.Format("[POLL SERVICE WORKER THREAD]: Error ", e)); + } + + PollServiceArgs.RequestsHandled++; + } + } } } \ No newline at end of file diff --git a/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs b/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs index 5406f004ed..44f704537b 100644 --- a/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs +++ b/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs @@ -64,14 +64,17 @@ namespace OpenSim.Framework.Servers.HttpServer m_server = pSrv; m_WorkerThreadCount = pWorkerThreadCount; m_workerThreads = new Thread[m_WorkerThreadCount]; + } + public void Start() + { //startup worker threads for (uint i = 0; i < m_WorkerThreadCount; i++) { m_workerThreads[i] = Watchdog.StartThread( PoolWorkerJob, - String.Format("PollServiceWorkerThread{0}", i), + string.Format("PollServiceWorkerThread{0}:{1}", i, m_server.Port), ThreadPriority.Normal, false, false, @@ -81,7 +84,7 @@ namespace OpenSim.Framework.Servers.HttpServer m_retrysThread = Watchdog.StartThread( this.CheckRetries, - "PollServiceWatcherThread", + string.Format("PollServiceWatcherThread:{0}", m_server.Port), ThreadPriority.Normal, false, true, @@ -89,7 +92,6 @@ namespace OpenSim.Framework.Servers.HttpServer 1000 * 60 * 10); } - private void ReQueueEvent(PollServiceHttpRequest req) { if (m_running) @@ -103,7 +105,7 @@ namespace OpenSim.Framework.Servers.HttpServer { if (m_running) { - if (req.PollServiceArgs.Type != PollServiceEventArgs.EventType.Normal) + if (req.PollServiceArgs.Type != PollServiceEventArgs.EventType.LongPoll) { m_requests.Enqueue(req); } @@ -140,13 +142,13 @@ namespace OpenSim.Framework.Servers.HttpServer } } - ~PollServiceRequestManager() + public void Stop() { m_running = false; Thread.Sleep(1000); // let the world move foreach (Thread t in m_workerThreads) - Watchdog.AbortThread(t.ManagedThreadId); + Watchdog.AbortThread(t.ManagedThreadId); try { @@ -205,7 +207,7 @@ namespace OpenSim.Framework.Servers.HttpServer if (responsedata == null) continue; - if (req.PollServiceArgs.Type == PollServiceEventArgs.EventType.Normal) // This is the event queue + if (req.PollServiceArgs.Type == PollServiceEventArgs.EventType.LongPoll) // This is the event queue { try { diff --git a/OpenSim/Framework/Servers/HttpServer/Properties/AssemblyInfo.cs b/OpenSim/Framework/Servers/HttpServer/Properties/AssemblyInfo.cs index 8e592c1e07..f409667bc8 100644 --- a/OpenSim/Framework/Servers/HttpServer/Properties/AssemblyInfo.cs +++ b/OpenSim/Framework/Servers/HttpServer/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; // Build Number // Revision // -[assembly: AssemblyVersion("0.7.6.*")] +[assembly: AssemblyVersion("0.8.0.*")] diff --git a/OpenSim/Framework/Servers/HttpServer/RestDeserialiseHandler.cs b/OpenSim/Framework/Servers/HttpServer/RestDeserialiseHandler.cs index 07082a8ebb..bd55657815 100644 --- a/OpenSim/Framework/Servers/HttpServer/RestDeserialiseHandler.cs +++ b/OpenSim/Framework/Servers/HttpServer/RestDeserialiseHandler.cs @@ -33,7 +33,7 @@ namespace OpenSim.Framework.Servers.HttpServer { public delegate TResponse RestDeserialiseMethod(TRequest request); - public class RestDeserialiseHandler : BaseRequestHandler, IStreamHandler + public class RestDeserialiseHandler : BaseOutputStreamHandler, IStreamHandler where TRequest : new() { private RestDeserialiseMethod m_method; @@ -48,7 +48,7 @@ namespace OpenSim.Framework.Servers.HttpServer m_method = method; } - public void Handle(string path, Stream request, Stream responseStream, + protected override void ProcessRequest(string path, Stream request, Stream responseStream, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) { TRequest deserial; diff --git a/OpenSim/Framework/Servers/HttpServer/RestSessionService.cs b/OpenSim/Framework/Servers/HttpServer/RestSessionService.cs index edcd134cae..83c9848e0d 100644 --- a/OpenSim/Framework/Servers/HttpServer/RestSessionService.cs +++ b/OpenSim/Framework/Servers/HttpServer/RestSessionService.cs @@ -183,7 +183,7 @@ namespace OpenSim.Framework.Servers.HttpServer public delegate bool CheckIdentityMethod(string sid, string aid); - public class RestDeserialiseSecureHandler : BaseRequestHandler, IStreamHandler + public class RestDeserialiseSecureHandler : BaseOutputStreamHandler, IStreamHandler where TRequest : new() { private static readonly ILog m_log @@ -201,7 +201,7 @@ namespace OpenSim.Framework.Servers.HttpServer m_method = method; } - public void Handle(string path, Stream request, Stream responseStream, + protected override void ProcessRequest(string path, Stream request, Stream responseStream, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) { RestSessionObject deserial = default(RestSessionObject); @@ -237,7 +237,7 @@ namespace OpenSim.Framework.Servers.HttpServer public delegate bool CheckTrustedSourceMethod(IPEndPoint peer); - public class RestDeserialiseTrustedHandler : BaseRequestHandler, IStreamHandler + public class RestDeserialiseTrustedHandler : BaseOutputStreamHandler, IStreamHandler where TRequest : new() { private static readonly ILog m_log @@ -260,7 +260,7 @@ namespace OpenSim.Framework.Servers.HttpServer m_method = method; } - public void Handle(string path, Stream request, Stream responseStream, + protected override void ProcessRequest(string path, Stream request, Stream responseStream, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) { TRequest deserial = default(TRequest); @@ -292,6 +292,5 @@ namespace OpenSim.Framework.Servers.HttpServer serializer.Serialize(xmlWriter, response); } } - } - -} + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Servers/HttpServer/RestStreamHandler.cs b/OpenSim/Framework/Servers/HttpServer/RestStreamHandler.cs index 1f17fee0d0..0305dee118 100644 --- a/OpenSim/Framework/Servers/HttpServer/RestStreamHandler.cs +++ b/OpenSim/Framework/Servers/HttpServer/RestStreamHandler.cs @@ -48,7 +48,7 @@ namespace OpenSim.Framework.Servers.HttpServer m_restMethod = restMethod; } - public override byte[] Handle(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + protected override byte[] ProcessRequest(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) { Encoding encoding = Encoding.UTF8; StreamReader streamReader = new StreamReader(request, encoding); diff --git a/OpenSim/Framework/Servers/HttpServer/WebsocketServerHandler.cs b/OpenSim/Framework/Servers/HttpServer/WebsocketServerHandler.cs index ee96b47c69..c2925e33f0 100644 --- a/OpenSim/Framework/Servers/HttpServer/WebsocketServerHandler.cs +++ b/OpenSim/Framework/Servers/HttpServer/WebsocketServerHandler.cs @@ -28,8 +28,10 @@ using System; using System.Collections.Generic; using System.IO; +using System.Net; using System.Security.Cryptography; using System.Text; +using System.Threading; using HttpServer; namespace OpenSim.Framework.Servers.HttpServer @@ -75,7 +77,7 @@ namespace OpenSim.Framework.Servers.HttpServer /// /// This is a regular HTTP Request... This may be removed in the future. /// - public event RegularHttpRequestDelegate OnRegularHttpRequest; +// public event RegularHttpRequestDelegate OnRegularHttpRequest; /// /// When the upgrade from a HTTP request to a Websocket is completed, this will be fired @@ -98,6 +100,8 @@ namespace OpenSim.Framework.Servers.HttpServer /// public ValidateHandshake HandshakeValidateMethodOverride = null; + private ManualResetEvent _receiveDone = new ManualResetEvent(false); + private OSHttpRequest _request; private HTTPNetworkContext _networkContext; private IHttpClientContext _clientContext; @@ -109,6 +113,8 @@ namespace OpenSim.Framework.Servers.HttpServer private bool _closing; private bool _upgraded; private int _maxPayloadBytes = 41943040; + private int _initialMsgTimeout = 0; + private int _defaultReadTimeout = 10000; private const string HandshakeAcceptText = "HTTP/1.1 101 Switching Protocols\r\n" + @@ -131,6 +137,7 @@ namespace OpenSim.Framework.Servers.HttpServer { _request = preq; _networkContext = pContext.GiveMeTheNetworkStreamIKnowWhatImDoing(); + _networkContext.Stream.ReadTimeout = _defaultReadTimeout; _clientContext = pContext; _bufferLength = bufferlen; _buffer = new byte[_bufferLength]; @@ -205,6 +212,16 @@ namespace OpenSim.Framework.Servers.HttpServer set { _maxPayloadBytes = value; } } + /// + /// Set this to the maximum amount of milliseconds to wait for the first complete message to be sent or received on the websocket after upgrading + /// Default, it waits forever. Use this when your Websocket consuming code opens a connection and waits for a message from the other side to avoid a Denial of Service vector. + /// + public int InitialMsgTimeout + { + get { return _initialMsgTimeout; } + set { _initialMsgTimeout = value; } + } + /// /// This triggers the websocket start the upgrade process /// @@ -245,6 +262,7 @@ namespace OpenSim.Framework.Servers.HttpServer string rawaccept = string.Format(HandshakeAcceptText, acceptKey); SendUpgradeSuccess(rawaccept); + } else { @@ -258,6 +276,10 @@ namespace OpenSim.Framework.Servers.HttpServer SendUpgradeSuccess(rawaccept); } } + public IPEndPoint GetRemoteIPEndpoint() + { + return _request.RemoteIPEndPoint; + } /// /// Generates a handshake response key string based on the client's @@ -290,9 +312,16 @@ namespace OpenSim.Framework.Servers.HttpServer WebSocketState socketState = new WebSocketState() { ReceivedBytes = new List(), Header = WebsocketFrameHeader.HeaderDefault(), FrameComplete = true}; byte[] bhandshakeResponse = Encoding.UTF8.GetBytes(pHandshakeResponse); + + + + try { - + if (_initialMsgTimeout > 0) + { + _receiveDone.Reset(); + } // Begin reading the TCP stream before writing the Upgrade success message to the other side of the stream. _networkContext.Stream.BeginRead(_buffer, 0, _bufferLength, OnReceive, socketState); @@ -303,16 +332,20 @@ namespace OpenSim.Framework.Servers.HttpServer UpgradeCompletedDelegate d = OnUpgradeCompleted; if (d != null) d(this, new UpgradeCompletedEventArgs()); + if (_initialMsgTimeout > 0) + { + if (!_receiveDone.WaitOne(TimeSpan.FromMilliseconds(_initialMsgTimeout))) + Close(string.Empty); + } } - catch (IOException fail) + catch (IOException) { Close(string.Empty); } - catch (ObjectDisposedException fail) + catch (ObjectDisposedException) { Close(string.Empty); - } - + } } /// @@ -414,8 +447,6 @@ namespace OpenSim.Framework.Servers.HttpServer _socketState.Header = pheader; } - - if (_socketState.FrameComplete) { ProcessFrame(_socketState); @@ -424,7 +455,6 @@ namespace OpenSim.Framework.Servers.HttpServer _socketState.ExpectedBytes = 0; } - } } else @@ -457,8 +487,7 @@ namespace OpenSim.Framework.Servers.HttpServer _socketState.ReceivedBytes.Clear(); _socketState.ExpectedBytes = 0; // do some processing - } - + } } } if (offset > 0) @@ -477,13 +506,12 @@ namespace OpenSim.Framework.Servers.HttpServer { // We can't read the stream anymore... } - } - catch (IOException fail) + catch (IOException) { Close(string.Empty); } - catch (ObjectDisposedException fail) + catch (ObjectDisposedException) { Close(string.Empty); } @@ -495,6 +523,11 @@ namespace OpenSim.Framework.Servers.HttpServer /// the string message that is to be sent public void SendMessage(string message) { + if (_initialMsgTimeout > 0) + { + _receiveDone.Set(); + _initialMsgTimeout = 0; + } byte[] messagedata = Encoding.UTF8.GetBytes(message); WebSocketFrame textMessageFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = messagedata }; textMessageFrame.Header.Opcode = WebSocketReader.OpCode.Text; @@ -505,6 +538,11 @@ namespace OpenSim.Framework.Servers.HttpServer public void SendData(byte[] data) { + if (_initialMsgTimeout > 0) + { + _receiveDone.Set(); + _initialMsgTimeout = 0; + } WebSocketFrame dataMessageFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = data}; dataMessageFrame.Header.IsEnd = true; dataMessageFrame.Header.Opcode = WebSocketReader.OpCode.Binary; @@ -537,6 +575,11 @@ namespace OpenSim.Framework.Servers.HttpServer /// public void SendPingCheck() { + if (_initialMsgTimeout > 0) + { + _receiveDone.Set(); + _initialMsgTimeout = 0; + } WebSocketFrame pingFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = new byte[0] }; pingFrame.Header.Opcode = WebSocketReader.OpCode.Ping; pingFrame.Header.IsEnd = true; @@ -550,6 +593,11 @@ namespace OpenSim.Framework.Servers.HttpServer /// public void Close(string message) { + if (_initialMsgTimeout > 0) + { + _receiveDone.Set(); + _initialMsgTimeout = 0; + } if (_networkContext == null) return; if (_networkContext.Stream != null) @@ -589,7 +637,11 @@ namespace OpenSim.Framework.Servers.HttpServer WebSocketReader.Mask(psocketState.Header.Mask, unmask); psocketState.ReceivedBytes = new List(unmask); } - + if (psocketState.Header.Opcode != WebSocketReader.OpCode.Continue && _initialMsgTimeout > 0) + { + _receiveDone.Set(); + _initialMsgTimeout = 0; + } switch (psocketState.Header.Opcode) { case WebSocketReader.OpCode.Ping: @@ -702,6 +754,11 @@ namespace OpenSim.Framework.Servers.HttpServer } public void Dispose() { + if (_initialMsgTimeout > 0) + { + _receiveDone.Set(); + _initialMsgTimeout = 0; + } if (_networkContext != null && _networkContext.Stream != null) { if (_networkContext.Stream.CanWrite) diff --git a/OpenSim/Framework/Servers/HttpServer/XmlRpcBasicDOSProtector.cs b/OpenSim/Framework/Servers/HttpServer/XmlRpcBasicDOSProtector.cs new file mode 100644 index 0000000000..f2122080c0 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/XmlRpcBasicDOSProtector.cs @@ -0,0 +1,91 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System.Net; +using Nwc.XmlRpc; +using OpenSim.Framework; + + +namespace OpenSim.Framework.Servers.HttpServer +{ + public class XmlRpcBasicDOSProtector + { + private readonly XmlRpcMethod _normalMethod; + private readonly XmlRpcMethod _throttledMethod; + + private readonly BasicDosProtectorOptions _options; + private readonly BasicDOSProtector _dosProtector; + + public XmlRpcBasicDOSProtector(XmlRpcMethod normalMethod, XmlRpcMethod throttledMethod,BasicDosProtectorOptions options) + { + _normalMethod = normalMethod; + _throttledMethod = throttledMethod; + + _options = options; + _dosProtector = new BasicDOSProtector(_options); + + } + public XmlRpcResponse Process(XmlRpcRequest request, IPEndPoint client) + { + + XmlRpcResponse resp = null; + string clientstring = GetClientString(request, client); + string endpoint = GetEndPoint(request, client); + if (_dosProtector.Process(clientstring, endpoint)) + resp = _normalMethod(request, client); + else + resp = _throttledMethod(request, client); + if (_options.MaxConcurrentSessions > 0) + _dosProtector.ProcessEnd(clientstring, endpoint); + return resp; + } + + private string GetClientString(XmlRpcRequest request, IPEndPoint client) + { + string clientstring; + if (_options.AllowXForwardedFor && request.Params.Count > 3) + { + object headerstr = request.Params[3]; + if (headerstr != null && !string.IsNullOrEmpty(headerstr.ToString())) + clientstring = request.Params[3].ToString(); + else + clientstring = client.Address.ToString(); + } + else + clientstring = client.Address.ToString(); + return clientstring; + } + + private string GetEndPoint(XmlRpcRequest request, IPEndPoint client) + { + return client.Address.ToString(); + } + + } + + +} diff --git a/OpenSim/Framework/Servers/MainServer.cs b/OpenSim/Framework/Servers/MainServer.cs index cfd34bb5b4..57931d449d 100644 --- a/OpenSim/Framework/Servers/MainServer.cs +++ b/OpenSim/Framework/Servers/MainServer.cs @@ -121,12 +121,14 @@ namespace OpenSim.Framework.Servers + " level >= 2 then long warnings are logged when receiving bad input data.\n" + " level >= 3 then short notices about all incoming non-poll HTTP requests are logged.\n" + " level >= 4 then the time taken to fulfill the request is logged.\n" - + " level >= 5 then a sample from the beginning of the incoming data is logged.\n" - + " level >= 6 then the entire incoming data is logged.\n" + + " level >= 5 then a sample from the beginning of the data is logged.\n" + + " level >= 6 then the entire data is logged.\n" + " no level is specified then the current level is returned.\n\n" + "If out or all and\n" + " level >= 3 then short notices about all outgoing requests going through WebUtil are logged.\n" - + " level >= 4 then the time taken to fulfill the request is logged.\n", + + " level >= 4 then the time taken to fulfill the request is logged.\n" + + " level >= 5 then a sample from the beginning of the data is logged.\n" + + " level >= 6 then the entire data is logged.\n", HandleDebugHttpCommand); } @@ -283,7 +285,12 @@ namespace OpenSim.Framework.Servers public static bool RemoveHttpServer(uint port) { lock (m_Servers) + { + if (instance != null && instance.Port == port) + instance = null; + return m_Servers.Remove(port); + } } /// diff --git a/OpenSim/Framework/Servers/ServerBase.cs b/OpenSim/Framework/Servers/ServerBase.cs index 1ff8acae78..7108314e0d 100644 --- a/OpenSim/Framework/Servers/ServerBase.cs +++ b/OpenSim/Framework/Servers/ServerBase.cs @@ -62,6 +62,8 @@ namespace OpenSim.Framework.Servers protected string m_pidFile = String.Empty; + protected ServerStatsCollector m_serverStatsCollector; + /// /// Server version information. Usually VersionInfo + information about git commit, operating system, etc. /// @@ -76,6 +78,11 @@ namespace OpenSim.Framework.Servers protected void CreatePIDFile(string path) { + if (File.Exists(path)) + m_log.ErrorFormat( + "[SERVER BASE]: Previous pid file {0} still exists on startup. Possibly previously unclean shutdown.", + path); + try { string pidstring = System.Diagnostics.Process.GetCurrentProcess().Id.ToString(); @@ -239,7 +246,7 @@ namespace OpenSim.Framework.Servers "Show thread status", HandleShow); m_console.Commands.AddCommand( - "General", false, "threads abort", + "Debug", false, "threads abort", "threads abort ", "Abort a managed thread. Use \"show threads\" to find possible threads.", HandleThreadsAbort); @@ -249,11 +256,180 @@ namespace OpenSim.Framework.Servers "Show thread status. Synonym for \"show threads\"", (string module, string[] args) => Notice(GetThreadsReport())); + m_console.Commands.AddCommand ( + "Debug", false, "debug comms set", + "debug comms set serialosdreq true|false", + "Set comms parameters. For debug purposes.", + HandleDebugCommsSet); + + m_console.Commands.AddCommand ( + "Debug", false, "debug comms status", + "debug comms status", + "Show current debug comms parameters.", + HandleDebugCommsStatus); + + m_console.Commands.AddCommand ( + "Debug", false, "debug threadpool set", + "debug threadpool set worker|iocp min|max ", + "Set threadpool parameters. For debug purposes.", + HandleDebugThreadpoolSet); + + m_console.Commands.AddCommand ( + "Debug", false, "debug threadpool status", + "debug threadpool status", + "Show current debug threadpool parameters.", + HandleDebugThreadpoolStatus); + m_console.Commands.AddCommand( - "General", false, "force gc", + "Debug", false, "force gc", "force gc", "Manually invoke runtime garbage collection. For debugging purposes", HandleForceGc); + + m_console.Commands.AddCommand( + "General", false, "quit", + "quit", + "Quit the application", (mod, args) => Shutdown()); + + m_console.Commands.AddCommand( + "General", false, "shutdown", + "shutdown", + "Quit the application", (mod, args) => Shutdown()); + + ChecksManager.RegisterConsoleCommands(m_console); + StatsManager.RegisterConsoleCommands(m_console); + } + + public void RegisterCommonComponents(IConfigSource configSource) + { + IConfig networkConfig = configSource.Configs["Network"]; + + if (networkConfig != null) + { + WebUtil.SerializeOSDRequestsPerEndpoint = networkConfig.GetBoolean("SerializeOSDRequests", false); + } + + m_serverStatsCollector = new ServerStatsCollector(); + m_serverStatsCollector.Initialise(configSource); + m_serverStatsCollector.Start(); + } + + private void HandleDebugCommsStatus(string module, string[] args) + { + Notice("serialosdreq is {0}", WebUtil.SerializeOSDRequestsPerEndpoint); + } + + private void HandleDebugCommsSet(string module, string[] args) + { + if (args.Length != 5) + { + Notice("Usage: debug comms set serialosdreq true|false"); + return; + } + + if (args[3] != "serialosdreq") + { + Notice("Usage: debug comms set serialosdreq true|false"); + return; + } + + bool setSerializeOsdRequests; + + if (!ConsoleUtil.TryParseConsoleBool(m_console, args[4], out setSerializeOsdRequests)) + return; + + WebUtil.SerializeOSDRequestsPerEndpoint = setSerializeOsdRequests; + + Notice("serialosdreq is now {0}", setSerializeOsdRequests); + } + + private void HandleDebugThreadpoolStatus(string module, string[] args) + { + int workerThreads, iocpThreads; + + ThreadPool.GetMinThreads(out workerThreads, out iocpThreads); + Notice("Min worker threads: {0}", workerThreads); + Notice("Min IOCP threads: {0}", iocpThreads); + + ThreadPool.GetMaxThreads(out workerThreads, out iocpThreads); + Notice("Max worker threads: {0}", workerThreads); + Notice("Max IOCP threads: {0}", iocpThreads); + + ThreadPool.GetAvailableThreads(out workerThreads, out iocpThreads); + Notice("Available worker threads: {0}", workerThreads); + Notice("Available IOCP threads: {0}", iocpThreads); + } + + private void HandleDebugThreadpoolSet(string module, string[] args) + { + if (args.Length != 6) + { + Notice("Usage: debug threadpool set worker|iocp min|max "); + return; + } + + int newThreads; + + if (!ConsoleUtil.TryParseConsoleInt(m_console, args[5], out newThreads)) + return; + + string poolType = args[3]; + string bound = args[4]; + + bool fail = false; + int workerThreads, iocpThreads; + + if (poolType == "worker") + { + if (bound == "min") + { + ThreadPool.GetMinThreads(out workerThreads, out iocpThreads); + + if (!ThreadPool.SetMinThreads(newThreads, iocpThreads)) + fail = true; + } + else + { + ThreadPool.GetMaxThreads(out workerThreads, out iocpThreads); + + if (!ThreadPool.SetMaxThreads(newThreads, iocpThreads)) + fail = true; + } + } + else + { + if (bound == "min") + { + ThreadPool.GetMinThreads(out workerThreads, out iocpThreads); + + if (!ThreadPool.SetMinThreads(workerThreads, newThreads)) + fail = true; + } + else + { + ThreadPool.GetMaxThreads(out workerThreads, out iocpThreads); + + if (!ThreadPool.SetMaxThreads(workerThreads, newThreads)) + fail = true; + } + } + + if (fail) + { + Notice("ERROR: Could not set {0} {1} threads to {2}", poolType, bound, newThreads); + } + else + { + int minWorkerThreads, maxWorkerThreads, minIocpThreads, maxIocpThreads; + + ThreadPool.GetMinThreads(out minWorkerThreads, out minIocpThreads); + ThreadPool.GetMaxThreads(out maxWorkerThreads, out maxIocpThreads); + + Notice("Min worker threads now {0}", minWorkerThreads); + Notice("Min IOCP threads now {0}", minIocpThreads); + Notice("Max worker threads now {0}", maxWorkerThreads); + Notice("Max IOCP threads now {0}", maxIocpThreads); + } } private void HandleForceGc(string module, string[] args) @@ -641,7 +817,68 @@ namespace OpenSim.Framework.Servers sb.AppendFormat("Total threads active: {0}\n\n", totalThreads); sb.Append("Main threadpool (excluding script engine pools)\n"); - sb.Append(Util.GetThreadPoolReport()); + sb.Append(GetThreadPoolReport()); + + return sb.ToString(); + } + + /// + /// Get a thread pool report. + /// + /// + public static string GetThreadPoolReport() + { + string threadPoolUsed = null; + int maxThreads = 0; + int minThreads = 0; + int allocatedThreads = 0; + int inUseThreads = 0; + int waitingCallbacks = 0; + int completionPortThreads = 0; + + StringBuilder sb = new StringBuilder(); + if (Util.FireAndForgetMethod == FireAndForgetMethod.SmartThreadPool) + { + STPInfo stpi = Util.GetSmartThreadPoolInfo(); + + // ROBUST currently leaves this the FireAndForgetMethod but never actually initializes the threadpool. + if (stpi != null) + { + threadPoolUsed = "SmartThreadPool"; + maxThreads = stpi.MaxThreads; + minThreads = stpi.MinThreads; + inUseThreads = stpi.InUseThreads; + allocatedThreads = stpi.ActiveThreads; + waitingCallbacks = stpi.WaitingCallbacks; + } + } + else if ( + Util.FireAndForgetMethod == FireAndForgetMethod.QueueUserWorkItem + || Util.FireAndForgetMethod == FireAndForgetMethod.UnsafeQueueUserWorkItem) + { + threadPoolUsed = "BuiltInThreadPool"; + ThreadPool.GetMaxThreads(out maxThreads, out completionPortThreads); + ThreadPool.GetMinThreads(out minThreads, out completionPortThreads); + int availableThreads; + ThreadPool.GetAvailableThreads(out availableThreads, out completionPortThreads); + inUseThreads = maxThreads - availableThreads; + allocatedThreads = -1; + waitingCallbacks = -1; + } + + if (threadPoolUsed != null) + { + sb.AppendFormat("Thread pool used : {0}\n", threadPoolUsed); + sb.AppendFormat("Max threads : {0}\n", maxThreads); + sb.AppendFormat("Min threads : {0}\n", minThreads); + sb.AppendFormat("Allocated threads : {0}\n", allocatedThreads < 0 ? "not applicable" : allocatedThreads.ToString()); + sb.AppendFormat("In use threads : {0}\n", inUseThreads); + sb.AppendFormat("Work items waiting : {0}\n", waitingCallbacks < 0 ? "not available" : waitingCallbacks.ToString()); + } + else + { + sb.AppendFormat("Thread pool not used\n"); + } return sb.ToString(); } @@ -693,5 +930,16 @@ namespace OpenSim.Framework.Servers if (m_console != null) m_console.OutputFormat(format, components); } + + public virtual void Shutdown() + { + m_serverStatsCollector.Close(); + ShutdownSpecific(); + } + + /// + /// Should be overriden and referenced by descendents if they need to perform extra shutdown processing + /// + protected virtual void ShutdownSpecific() {} } } diff --git a/OpenSim/Framework/Servers/VersionInfo.cs b/OpenSim/Framework/Servers/VersionInfo.cs index 737c14dcdd..33b1366376 100644 --- a/OpenSim/Framework/Servers/VersionInfo.cs +++ b/OpenSim/Framework/Servers/VersionInfo.cs @@ -29,7 +29,7 @@ namespace OpenSim { public class VersionInfo { - private const string VERSION_NUMBER = "0.7.6CM"; + private const string VERSION_NUMBER = "0.8.0CM"; private const Flavour VERSION_FLAVOUR = Flavour.Dev; public enum Flavour diff --git a/OpenSim/Framework/TaskInventoryItem.cs b/OpenSim/Framework/TaskInventoryItem.cs index 574ee567f4..2ec4bd1af3 100644 --- a/OpenSim/Framework/TaskInventoryItem.cs +++ b/OpenSim/Framework/TaskInventoryItem.cs @@ -124,7 +124,7 @@ namespace OpenSim.Framework { get { - if (_creatorData != null && _creatorData != string.Empty) + if (!string.IsNullOrEmpty(_creatorData)) return _creatorID.ToString() + ';' + _creatorData; else return _creatorID.ToString(); diff --git a/OpenSim/Framework/Tests/LocationTest.cs b/OpenSim/Framework/Tests/LocationTest.cs index a56ecb4049..3d5d1d275b 100644 --- a/OpenSim/Framework/Tests/LocationTest.cs +++ b/OpenSim/Framework/Tests/LocationTest.cs @@ -51,21 +51,28 @@ namespace OpenSim.Framework.Tests [Test] public void locationXYRegionHandle() { - Location TestLocation1 = new Location(256000,256000); - Location TestLocation2 = new Location(1099511628032000); + Location TestLocation1 = new Location(255000,256000); + Location TestLocation2 = new Location(1095216660736000); Assert.That(TestLocation1 == TestLocation2); - Assert.That(TestLocation2.X == 256000 && TestLocation2.Y == 256000, "Test xy location doesn't match regionhandle provided"); + Assert.That(TestLocation1.X == 255000 && TestLocation1.Y == 256000, "Test xy location doesn't match position in the constructor"); + Assert.That(TestLocation2.X == 255000 && TestLocation2.Y == 256000, "Test xy location doesn't match regionhandle provided"); - Assert.That(TestLocation2.RegionHandle == 1099511628032000, + Assert.That(TestLocation2.RegionHandle == 1095216660736000, "Location RegionHandle Property didn't match regionhandle provided in constructor"); + ulong RegionHandle = TestLocation1.RegionHandle; + Assert.That(RegionHandle.Equals(1095216660736000), "Equals(regionhandle) failed to match the position in the constructor"); - TestLocation1 = new Location(256001, 256001); - TestLocation2 = new Location(1099511628032000); + TestLocation2 = new Location(RegionHandle); + Assert.That(TestLocation2.Equals(255000, 256000), "Decoded regionhandle failed to match the original position in the constructor"); + + + TestLocation1 = new Location(255001, 256001); + TestLocation2 = new Location(1095216660736000); Assert.That(TestLocation1 != TestLocation2); - Assert.That(TestLocation1.Equals(256001, 256001), "Equals(x,y) failed to match the position in the constructor"); + Assert.That(TestLocation1.Equals(255001, 256001), "Equals(x,y) failed to match the position in the constructor"); Assert.That(TestLocation2.GetHashCode() == (TestLocation2.X.GetHashCode() ^ TestLocation2.Y.GetHashCode()), "GetHashCode failed to produce the expected hashcode"); diff --git a/OpenSim/Framework/Tests/MundaneFrameworkTests.cs b/OpenSim/Framework/Tests/MundaneFrameworkTests.cs index 0fbdaf3af0..08f2af53f2 100644 --- a/OpenSim/Framework/Tests/MundaneFrameworkTests.cs +++ b/OpenSim/Framework/Tests/MundaneFrameworkTests.cs @@ -100,7 +100,7 @@ namespace OpenSim.Framework.Tests cadu.AVHeight = Size1.Z; AgentPosition position2 = new AgentPosition(); - position2.CopyFrom(cadu); + position2.CopyFrom(cadu, position1.SessionID); Assert.IsTrue( position2.AgentID == position1.AgentID diff --git a/OpenSim/Framework/Tests/UtilTest.cs b/OpenSim/Framework/Tests/UtilTest.cs index 11ca0683d3..3b7f25299f 100644 --- a/OpenSim/Framework/Tests/UtilTest.cs +++ b/OpenSim/Framework/Tests/UtilTest.cs @@ -282,5 +282,87 @@ namespace OpenSim.Framework.Tests String.Format("Incorrect InventoryType mapped from Content-Type {0}", invcontenttypes[i])); } } + + [Test] + public void FakeParcelIDTests() + { + byte[] hexBytes8 = { 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10 }; + byte[] hexBytes16 = { + 0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, + 0x77, 0x69, 0x5a, 0x4b, 0x3c, 0x2d, 0x1e, 0x0f }; + UInt64 var64Bit = (UInt64)0xfedcba9876543210; + + //Region handle is for location 255000,256000. + ulong regionHandle1 = 1095216660736000; + uint x1 = 100; + uint y1 = 200; + uint z1 = 22; + ulong regionHandle2; + uint x2, y2, z2; + UUID fakeParcelID1, fakeParcelID2, uuid; + + ulong bigInt64 = Util.BytesToUInt64Big(hexBytes8); + Assert.AreEqual(var64Bit, bigInt64, + "BytesToUint64Bit conversion of 8 bytes to UInt64 failed."); + + //Test building and decoding using some typical input values + fakeParcelID1 = Util.BuildFakeParcelID(regionHandle1, x1, y1); + Util.ParseFakeParcelID(fakeParcelID1, out regionHandle2, out x2, out y2); + Assert.AreEqual(regionHandle1, regionHandle2, + "region handle decoded from FakeParcelID wth X/Y failed."); + Assert.AreEqual(x1, x2, + "X coordinate decoded from FakeParcelID wth X/Y failed."); + Assert.AreEqual(y1, y2, + "Y coordinate decoded from FakeParcelID wth X/Y failed."); + + fakeParcelID1 = Util.BuildFakeParcelID(regionHandle1, x1, y1, z1); + Util.ParseFakeParcelID(fakeParcelID1, out regionHandle2, out x2, out y2, out z2); + Assert.AreEqual(regionHandle1, regionHandle2, + "region handle decoded from FakeParcelID with X/Y/Z failed."); + Assert.AreEqual(x1, x2, + "X coordinate decoded from FakeParcelID with X/Y/Z failed."); + Assert.AreEqual(y1, y2, + "Y coordinate decoded from FakeParcelID with X/Y/Z failed."); + Assert.AreEqual(z1, z2, + "Z coordinate decoded from FakeParcelID with X/Y/Z failed."); + + //Do some more extreme tests to check the encoding and decoding + x1 = 0x55aa; + y1 = 0x9966; + z1 = 0x5a96; + + fakeParcelID1 = Util.BuildFakeParcelID(var64Bit, x1, y1); + Util.ParseFakeParcelID(fakeParcelID1, out regionHandle2, out x2, out y2); + Assert.AreEqual(var64Bit, regionHandle2, + "region handle decoded from FakeParcelID with X/Y/Z failed."); + Assert.AreEqual(x1, x2, + "X coordinate decoded from FakeParcelID with X/Y/Z failed."); + Assert.AreEqual(y1, y2, + "Y coordinate decoded from FakeParcelID with X/Y/Z failed."); + + fakeParcelID1 = Util.BuildFakeParcelID(var64Bit, x1, y1, z1); + Util.ParseFakeParcelID(fakeParcelID1, out regionHandle2, out x2, out y2, out z2); + Assert.AreEqual(var64Bit, regionHandle2, + "region handle decoded from FakeParcelID with X/Y/Z failed."); + Assert.AreEqual(x1, x2, + "X coordinate decoded from FakeParcelID with X/Y/Z failed."); + Assert.AreEqual(y1, y2, + "Y coordinate decoded from FakeParcelID with X/Y/Z failed."); + Assert.AreEqual(z1, z2, + "Z coordinate decoded from FakeParcelID with X/Y/Z failed."); + + + x1 = 64; + y1 = 192; + fakeParcelID1 = Util.BuildFakeParcelID(regionHandle1, x1, y1); + Util.FakeParcelIDToGlobalPosition(fakeParcelID1, out x2, out y2); + Assert.AreEqual(255000+x1, x2, + "Global X coordinate decoded from regionHandle failed."); + Assert.AreEqual(256000+y1, y2, + "Global Y coordinate decoded from regionHandle failed."); + + uuid = new UUID("00dd0700-00d1-0700-3800-000032000000"); + Util.FakeParcelIDToGlobalPosition(uuid, out x2, out y2); + } } } diff --git a/OpenSim/Framework/UserProfiles.cs b/OpenSim/Framework/UserProfiles.cs new file mode 100644 index 0000000000..61335917e1 --- /dev/null +++ b/OpenSim/Framework/UserProfiles.cs @@ -0,0 +1,117 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using OpenMetaverse; + +namespace OpenSim.Framework +{ + public class UserClassifiedAdd + { + public UUID ClassifiedId = UUID.Zero; + public UUID CreatorId = UUID.Zero; + public int CreationDate = 0; + public int ExpirationDate = 0; + public int Category = 0; + public string Name = string.Empty; + public string Description = string.Empty; + public UUID ParcelId = UUID.Zero; + public int ParentEstate = 0; + public UUID SnapshotId = UUID.Zero; + public string SimName = string.Empty; + public string GlobalPos = "<0,0,0>"; + public string ParcelName = string.Empty; + public byte Flags = 0; + public int Price = 0; + } + + public class UserProfileProperties + { + public UUID UserId = UUID.Zero; + public UUID PartnerId = UUID.Zero; + public bool PublishProfile = false; + public bool PublishMature = false; + public string WebUrl = string.Empty; + public int WantToMask = 0; + public string WantToText = string.Empty; + public int SkillsMask = 0; + public string SkillsText = string.Empty; + public string Language = string.Empty; + public UUID ImageId = UUID.Zero; + public string AboutText = string.Empty; + public UUID FirstLifeImageId = UUID.Zero; + public string FirstLifeText = string.Empty; + } + + public class UserProfilePick + { + public UUID PickId = UUID.Zero; + public UUID CreatorId = UUID.Zero; + public bool TopPick = false; + public string Name = string.Empty; + public string OriginalName = string.Empty; + public string Desc = string.Empty; + public UUID ParcelId = UUID.Zero; + public UUID SnapshotId = UUID.Zero; + public string User = string.Empty; + public string SimName = string.Empty; + public string GlobalPos = "<0,0,0>"; + public int SortOrder = 0; + public bool Enabled = false; + } + + public class UserProfileNotes + { + public UUID UserId; + public UUID TargetId; + public string Notes; + } + + public class UserAccountProperties + { + public string EmailAddress = string.Empty; + public string Firstname = string.Empty; + public string LastName = string.Empty; + public string Password = string.Empty; + public string UserId = string.Empty; + } + + public class UserAccountAuth + { + public string UserId = UUID.Zero.ToString(); + public string Password = string.Empty; + } + + public class UserAppData + { + public string TagId = string.Empty; + public string DataKey = string.Empty; + public string UserId = UUID.Zero.ToString(); + public string DataVal = string.Empty; + } +} + diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs index 557f38ec9b..1775fef8dc 100644 --- a/OpenSim/Framework/Util.cs +++ b/OpenSim/Framework/Util.cs @@ -88,10 +88,31 @@ namespace OpenSim.Framework Thread, } + /// + /// Class for delivering SmartThreadPool statistical information + /// + /// + /// We do it this way so that we do not directly expose STP. + /// + public class STPInfo + { + public string Name { get; set; } + public STPStartInfo STPStartInfo { get; set; } + public WIGStartInfo WIGStartInfo { get; set; } + public bool IsIdle { get; set; } + public bool IsShuttingDown { get; set; } + public int MaxThreads { get; set; } + public int MinThreads { get; set; } + public int InUseThreads { get; set; } + public int ActiveThreads { get; set; } + public int WaitingCallbacks { get; set; } + public int MaxConcurrentWorkItems { get; set; } + } + /// /// Miscellaneous utility functions /// - public class Util + public static class Util { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); @@ -109,7 +130,7 @@ namespace OpenSim.Framework private static SmartThreadPool m_ThreadPool; // Unix-epoch starts at January 1st 1970, 00:00:00 UTC. And all our times in the server are (or at least should be) in UTC. - private static readonly DateTime unixEpoch = + public static readonly DateTime UnixEpoch = DateTime.ParseExact("1970-01-01 00:00:00 +0", "yyyy-MM-dd hh:mm:ss z", DateTimeFormatInfo.InvariantInfo).ToUniversalTime(); private static readonly string rawUUIDPattern @@ -120,6 +141,11 @@ namespace OpenSim.Framework public static FireAndForgetMethod DefaultFireAndForgetMethod = FireAndForgetMethod.SmartThreadPool; public static FireAndForgetMethod FireAndForgetMethod = DefaultFireAndForgetMethod; + public static bool IsPlatformMono + { + get { return Type.GetType("Mono.Runtime") != null; } + } + /// /// Gets the name of the directory where the current running executable /// is located @@ -307,6 +333,49 @@ namespace OpenSim.Framework return Utils.UIntsToLong(X, Y); } + // Regions are identified with a 'handle' made up of its region coordinates packed into a ulong. + // Several places rely on the ability to extract a region's location from its handle. + // Note the location is in 'world coordinates' (see below). + // Region handles are based on the lowest coordinate of the region so trim the passed x,y to be the regions 0,0. + public static ulong RegionWorldLocToHandle(uint X, uint Y) + { + return Utils.UIntsToLong(X, Y); + } + + public static ulong RegionLocToHandle(uint X, uint Y) + { + return Utils.UIntsToLong(Util.RegionToWorldLoc(X), Util.RegionToWorldLoc(Y)); + } + + public static void RegionHandleToWorldLoc(ulong handle, out uint X, out uint Y) + { + X = (uint)(handle >> 32); + Y = (uint)(handle & (ulong)uint.MaxValue); + } + + public static void RegionHandleToRegionLoc(ulong handle, out uint X, out uint Y) + { + uint worldX, worldY; + RegionHandleToWorldLoc(handle, out worldX, out worldY); + X = WorldToRegionLoc(worldX); + Y = WorldToRegionLoc(worldY); + } + + // A region location can be 'world coordinates' (meters from zero) or 'region coordinates' + // (number of regions from zero). This measurement of regions relies on the legacy 256 region size. + // These routines exist to make what is being converted explicit so the next person knows what was meant. + // Convert a region's 'world coordinate' to its 'region coordinate'. + public static uint WorldToRegionLoc(uint worldCoord) + { + return worldCoord / Constants.RegionSize; + } + + // Convert a region's 'region coordinate' to its 'world coordinate'. + public static uint RegionToWorldLoc(uint regionCoord) + { + return regionCoord * Constants.RegionSize; + } + public static T Clamp(T x, T min, T max) where T : IComparable { @@ -495,20 +564,18 @@ namespace OpenSim.Framework public static int ToUnixTime(DateTime stamp) { - TimeSpan t = stamp.ToUniversalTime() - unixEpoch; - return (int) t.TotalSeconds; + TimeSpan t = stamp.ToUniversalTime() - UnixEpoch; + return (int)t.TotalSeconds; } public static DateTime ToDateTime(ulong seconds) { - DateTime epoch = unixEpoch; - return epoch.AddSeconds(seconds); + return UnixEpoch.AddSeconds(seconds); } public static DateTime ToDateTime(int seconds) { - DateTime epoch = unixEpoch; - return epoch.AddSeconds(seconds); + return UnixEpoch.AddSeconds(seconds); } /// @@ -976,7 +1043,7 @@ namespace OpenSim.Framework else if (typeof(T) == typeof(Int32)) val = cnf.GetInt(varname, (int)val); else if (typeof(T) == typeof(float)) - val = cnf.GetFloat(varname, (int)val); + val = cnf.GetFloat(varname, (float)val); else m_log.ErrorFormat("[UTIL]: Unhandled type {0}", typeof(T)); } @@ -1233,7 +1300,7 @@ namespace OpenSim.Framework byte[] bytes = { (byte)regionHandle, (byte)(regionHandle >> 8), (byte)(regionHandle >> 16), (byte)(regionHandle >> 24), - (byte)(regionHandle >> 32), (byte)(regionHandle >> 40), (byte)(regionHandle >> 48), (byte)(regionHandle << 56), + (byte)(regionHandle >> 32), (byte)(regionHandle >> 40), (byte)(regionHandle >> 48), (byte)(regionHandle >> 56), (byte)x, (byte)(x >> 8), 0, 0, (byte)y, (byte)(y >> 8), 0, 0 }; return new UUID(bytes, 0); @@ -1244,7 +1311,7 @@ namespace OpenSim.Framework byte[] bytes = { (byte)regionHandle, (byte)(regionHandle >> 8), (byte)(regionHandle >> 16), (byte)(regionHandle >> 24), - (byte)(regionHandle >> 32), (byte)(regionHandle >> 40), (byte)(regionHandle >> 48), (byte)(regionHandle << 56), + (byte)(regionHandle >> 32), (byte)(regionHandle >> 40), (byte)(regionHandle >> 48), (byte)(regionHandle >> 56), (byte)x, (byte)(x >> 8), (byte)z, (byte)(z >> 8), (byte)y, (byte)(y >> 8), 0, 0 }; return new UUID(bytes, 0); @@ -1317,7 +1384,7 @@ namespace OpenSim.Framework ru = "OSX/Mono"; else { - if (Type.GetType("Mono.Runtime") != null) + if (IsPlatformMono) ru = "Win/Mono"; else ru = "Win/.NET"; @@ -1765,10 +1832,12 @@ namespace OpenSim.Framework FireAndForget(callback, null); } - public static void InitThreadPool(int maxThreads) + public static void InitThreadPool(int minThreads, int maxThreads) { if (maxThreads < 2) throw new ArgumentOutOfRangeException("maxThreads", "maxThreads must be greater than 2"); + if (minThreads > maxThreads || minThreads < 2) + throw new ArgumentOutOfRangeException("minThreads", "minThreads must be greater than 2 and less than or equal to maxThreads"); if (m_ThreadPool != null) throw new InvalidOperationException("SmartThreadPool is already initialized"); @@ -1776,7 +1845,7 @@ namespace OpenSim.Framework startInfo.ThreadPoolName = "Util"; startInfo.IdleTimeout = 2000; startInfo.MaxWorkerThreads = maxThreads; - startInfo.MinWorkerThreads = 2; + startInfo.MinWorkerThreads = minThreads; m_ThreadPool = new SmartThreadPool(startInfo); } @@ -1851,8 +1920,8 @@ namespace OpenSim.Framework break; case FireAndForgetMethod.SmartThreadPool: if (m_ThreadPool == null) - InitThreadPool(15); - m_ThreadPool.QueueWorkItem(SmartThreadPoolCallback, new object[] { realCallback, obj }); + InitThreadPool(2, 15); + m_ThreadPool.QueueWorkItem((cb, o) => cb(o), realCallback, obj); break; case FireAndForgetMethod.Thread: Thread thread = new Thread(delegate(object o) { realCallback(o); }); @@ -1864,72 +1933,29 @@ namespace OpenSim.Framework } /// - /// Get a thread pool report. + /// Get information about the current state of the smart thread pool. /// - /// - public static string GetThreadPoolReport() + /// + /// null if this isn't the pool being used for non-scriptengine threads. + /// + public static STPInfo GetSmartThreadPoolInfo() { - string threadPoolUsed = null; - int maxThreads = 0; - int minThreads = 0; - int allocatedThreads = 0; - int inUseThreads = 0; - int waitingCallbacks = 0; - int completionPortThreads = 0; + if (m_ThreadPool == null) + return null; - StringBuilder sb = new StringBuilder(); - if (FireAndForgetMethod == FireAndForgetMethod.SmartThreadPool) - { - // ROBUST currently leaves this the FireAndForgetMethod but never actually initializes the threadpool. - if (m_ThreadPool != null) - { - threadPoolUsed = "SmartThreadPool"; - maxThreads = m_ThreadPool.MaxThreads; - minThreads = m_ThreadPool.MinThreads; - inUseThreads = m_ThreadPool.InUseThreads; - allocatedThreads = m_ThreadPool.ActiveThreads; - waitingCallbacks = m_ThreadPool.WaitingCallbacks; - } - } - else if ( - FireAndForgetMethod == FireAndForgetMethod.UnsafeQueueUserWorkItem - || FireAndForgetMethod == FireAndForgetMethod.UnsafeQueueUserWorkItem) - { - threadPoolUsed = "BuiltInThreadPool"; - ThreadPool.GetMaxThreads(out maxThreads, out completionPortThreads); - ThreadPool.GetMinThreads(out minThreads, out completionPortThreads); - int availableThreads; - ThreadPool.GetAvailableThreads(out availableThreads, out completionPortThreads); - inUseThreads = maxThreads - availableThreads; - allocatedThreads = -1; - waitingCallbacks = -1; - } + STPInfo stpi = new STPInfo(); + stpi.Name = m_ThreadPool.Name; + stpi.STPStartInfo = m_ThreadPool.STPStartInfo; + stpi.IsIdle = m_ThreadPool.IsIdle; + stpi.IsShuttingDown = m_ThreadPool.IsShuttingdown; + stpi.MaxThreads = m_ThreadPool.MaxThreads; + stpi.MinThreads = m_ThreadPool.MinThreads; + stpi.InUseThreads = m_ThreadPool.InUseThreads; + stpi.ActiveThreads = m_ThreadPool.ActiveThreads; + stpi.WaitingCallbacks = m_ThreadPool.WaitingCallbacks; + stpi.MaxConcurrentWorkItems = m_ThreadPool.Concurrency; - if (threadPoolUsed != null) - { - sb.AppendFormat("Thread pool used : {0}\n", threadPoolUsed); - sb.AppendFormat("Max threads : {0}\n", maxThreads); - sb.AppendFormat("Min threads : {0}\n", minThreads); - sb.AppendFormat("Allocated threads : {0}\n", allocatedThreads < 0 ? "not applicable" : allocatedThreads.ToString()); - sb.AppendFormat("In use threads : {0}\n", inUseThreads); - sb.AppendFormat("Work items waiting : {0}\n", waitingCallbacks < 0 ? "not available" : waitingCallbacks.ToString()); - } - else - { - sb.AppendFormat("Thread pool not used\n"); - } - - return sb.ToString(); - } - - private static object SmartThreadPoolCallback(object o) - { - object[] array = (object[])o; - WaitCallback callback = (WaitCallback)array[0]; - object obj = array[1]; - - callback(obj); - return null; + return stpi; } #endregion FireAndForget Threading Pattern @@ -2058,8 +2084,10 @@ namespace OpenSim.Framework #region Xml Serialization Utilities public static bool ReadBoolean(XmlTextReader reader) { + // AuroraSim uses "int" for some fields that are boolean in OpenSim, e.g. "PassCollisions". Don't fail because of this. reader.ReadStartElement(); - bool result = Boolean.Parse(reader.ReadContentAsString().ToLower()); + string val = reader.ReadContentAsString().ToLower(); + bool result = val.Equals("true") || val.Equals("1"); reader.ReadEndElement(); return result; @@ -2148,7 +2176,7 @@ namespace OpenSim.Framework /// the secret part public static bool ParseUniversalUserIdentifier(string value, out UUID uuid, out string url, out string firstname, out string lastname, out string secret) { - uuid = UUID.Zero; url = string.Empty; firstname = "Unknown"; lastname = "User"; secret = string.Empty; + uuid = UUID.Zero; url = string.Empty; firstname = "Unknown"; lastname = "UserUPUUI"; secret = string.Empty; string[] parts = value.Split(';'); if (parts.Length >= 1) @@ -2282,7 +2310,7 @@ namespace OpenSim.Framework { lock (m_syncRoot) { - m_lowQueue.Enqueue(data); + q.Enqueue(data); m_s.WaitOne(0); m_s.Release(); } @@ -2322,7 +2350,7 @@ namespace OpenSim.Framework { if (m_highQueue.Count > 0) res = m_highQueue.Dequeue(); - else + else if (m_lowQueue.Count > 0) res = m_lowQueue.Dequeue(); if (m_highQueue.Count == 0 && m_lowQueue.Count == 0) diff --git a/OpenSim/Framework/WebUtil.cs b/OpenSim/Framework/WebUtil.cs index bf57fd422d..33ef8e01c7 100644 --- a/OpenSim/Framework/WebUtil.cs +++ b/OpenSim/Framework/WebUtil.cs @@ -66,6 +66,11 @@ namespace OpenSim.Framework /// public static int RequestNumber { get; internal set; } + /// + /// Control where OSD requests should be serialized per endpoint. + /// + public static bool SerializeOSDRequestsPerEndpoint { get; set; } + /// /// this is the header field used to communicate the local request id /// used for performance and debugging @@ -145,10 +150,50 @@ namespace OpenSim.Framework public static OSDMap ServiceOSDRequest(string url, OSDMap data, string method, int timeout, bool compressed) { - lock (EndPointLock(url)) + if (SerializeOSDRequestsPerEndpoint) { - return ServiceOSDRequestWorker(url,data,method,timeout,compressed); + lock (EndPointLock(url)) + { + return ServiceOSDRequestWorker(url, data, method, timeout, compressed); + } } + else + { + return ServiceOSDRequestWorker(url, data, method, timeout, compressed); + } + } + + public static void LogOutgoingDetail(Stream outputStream) + { + using (StreamReader reader = new StreamReader(Util.Copy(outputStream), Encoding.UTF8)) + { + string output; + + if (DebugLevel == 5) + { + const int sampleLength = 80; + char[] sampleChars = new char[sampleLength]; + reader.Read(sampleChars, 0, sampleLength); + output = new string(sampleChars); + } + else + { + output = reader.ReadToEnd(); + } + + LogOutgoingDetail(output); + } + } + + public static void LogOutgoingDetail(string output) + { + if (DebugLevel == 5) + { + output = output.Substring(0, 80); + output = output + "..."; + } + + m_log.DebugFormat("[WEB UTIL]: {0}", output.Replace("\n", @"\n")); } private static OSDMap ServiceOSDRequestWorker(string url, OSDMap data, string method, int timeout, bool compressed) @@ -178,7 +223,11 @@ namespace OpenSim.Framework // If there is some input, write it into the request if (data != null) { - strBuffer = OSDParser.SerializeJsonString(data); + strBuffer = OSDParser.SerializeJsonString(data); + + if (DebugLevel >= 5) + LogOutgoingDetail(strBuffer); + byte[] buffer = System.Text.Encoding.UTF8.GetBytes(strBuffer); if (compressed) @@ -358,6 +407,10 @@ namespace OpenSim.Framework if (data != null) { queryString = BuildQueryString(data); + + if (DebugLevel >= 5) + LogOutgoingDetail(queryString); + byte[] buffer = System.Text.Encoding.UTF8.GetBytes(queryString); request.ContentLength = buffer.Length; @@ -668,7 +721,7 @@ namespace OpenSim.Framework /// public static string[] GetPreferredImageTypes(string accept) { - if (accept == null || accept == string.Empty) + if (string.IsNullOrEmpty(accept)) return new string[0]; string[] types = accept.Split(new char[] { ',' }); @@ -769,6 +822,9 @@ namespace OpenSim.Framework int length = (int)buffer.Length; request.ContentLength = length; + if (WebUtil.DebugLevel >= 5) + WebUtil.LogOutgoingDetail(buffer); + request.BeginGetRequestStream(delegate(IAsyncResult res) { Stream requestStream = request.EndGetRequestStream(res); @@ -928,11 +984,12 @@ namespace OpenSim.Framework /// /// /// + /// /// /// /// Thrown if we encounter a network issue while posting /// the request. You'll want to make sure you deal with this as they're not uncommon - public static string MakeRequest(string verb, string requestUrl, string obj) + public static string MakeRequest(string verb, string requestUrl, string obj, int timeoutsecs) { int reqnum = WebUtil.RequestNumber++; @@ -946,6 +1003,8 @@ namespace OpenSim.Framework WebRequest request = WebRequest.Create(requestUrl); request.Method = verb; + if (timeoutsecs > 0) + request.Timeout = timeoutsecs * 1000; string respstring = String.Empty; int tickset = Util.EnvironmentTickCountSubtract(tickstart); @@ -966,6 +1025,9 @@ namespace OpenSim.Framework length = (int)obj.Length; request.ContentLength = length; + if (WebUtil.DebugLevel >= 5) + WebUtil.LogOutgoingDetail(buffer); + Stream requestStream = null; try { @@ -1039,6 +1101,11 @@ namespace OpenSim.Framework return respstring; } + + public static string MakeRequest(string verb, string requestUrl, string obj) + { + return MakeRequest(verb, requestUrl, obj, -1); + } } public class SynchronousRestObjectRequester @@ -1111,6 +1178,9 @@ namespace OpenSim.Framework int length = (int)buffer.Length; request.ContentLength = length; + if (WebUtil.DebugLevel >= 5) + WebUtil.LogOutgoingDetail(buffer); + Stream requestStream = null; try { @@ -1213,4 +1283,4 @@ namespace OpenSim.Framework return deserial; } } -} +} \ No newline at end of file diff --git a/OpenSim/Region/Application/Application.cs b/OpenSim/Region/Application/Application.cs index c3e7ec2e67..3a4e5df48c 100644 --- a/OpenSim/Region/Application/Application.cs +++ b/OpenSim/Region/Application/Application.cs @@ -103,48 +103,63 @@ namespace OpenSim "[OPENSIM MAIN]: Environment variable MONO_THREADS_PER_CPU is {0}", monoThreadsPerCpu ?? "unset"); // Verify the Threadpool allocates or uses enough worker and IO completion threads - // .NET 2.0 workerthreads default to 50 * numcores - // .NET 3.0 workerthreads defaults to 250 * numcores - // .NET 4.0 workerthreads are dynamic based on bitness and OS resources - // Max IO Completion threads are 1000 on all 3 CLRs. + // .NET 2.0, workerthreads default to 50 * numcores + // .NET 3.0, workerthreads defaults to 250 * numcores + // .NET 4.0, workerthreads are dynamic based on bitness and OS resources + // Max IO Completion threads are 1000 on all 3 CLRs + // + // Mono 2.10.9 to at least Mono 3.1, workerthreads default to 100 * numcores, iocp threads to 4 * numcores int workerThreadsMin = 500; int workerThreadsMax = 1000; // may need further adjustment to match other CLR int iocpThreadsMin = 1000; int iocpThreadsMax = 2000; // may need further adjustment to match other CLR + + { + int currentMinWorkerThreads, currentMinIocpThreads; + System.Threading.ThreadPool.GetMinThreads(out currentMinWorkerThreads, out currentMinIocpThreads); + m_log.InfoFormat( + "[OPENSIM MAIN]: Runtime gave us {0} min worker threads and {1} min IOCP threads", + currentMinWorkerThreads, currentMinIocpThreads); + } + int workerThreads, iocpThreads; System.Threading.ThreadPool.GetMaxThreads(out workerThreads, out iocpThreads); - m_log.InfoFormat("[OPENSIM MAIN]: Runtime gave us {0} worker threads and {1} IOCP threads", workerThreads, iocpThreads); + m_log.InfoFormat("[OPENSIM MAIN]: Runtime gave us {0} max worker threads and {1} max IOCP threads", workerThreads, iocpThreads); + if (workerThreads < workerThreadsMin) { workerThreads = workerThreadsMin; - m_log.InfoFormat("[OPENSIM MAIN]: Bumping up to worker threads to {0}",workerThreads); + m_log.InfoFormat("[OPENSIM MAIN]: Bumping up to max worker threads to {0}",workerThreads); } if (workerThreads > workerThreadsMax) { workerThreads = workerThreadsMax; - m_log.InfoFormat("[OPENSIM MAIN]: Limiting worker threads to {0}",workerThreads); + m_log.InfoFormat("[OPENSIM MAIN]: Limiting max worker threads to {0}",workerThreads); } + // Increase the number of IOCP threads available. // Mono defaults to a tragically low number (24 on 6-core / 8GB Fedora 17) if (iocpThreads < iocpThreadsMin) { iocpThreads = iocpThreadsMin; - m_log.InfoFormat("[OPENSIM MAIN]: Bumping up IO completion threads to {0}",iocpThreads); + m_log.InfoFormat("[OPENSIM MAIN]: Bumping up max IOCP threads to {0}",iocpThreads); } // Make sure we don't overallocate IOCP threads and thrash system resources if ( iocpThreads > iocpThreadsMax ) { iocpThreads = iocpThreadsMax; - m_log.InfoFormat("[OPENSIM MAIN]: Limiting IO completion threads to {0}",iocpThreads); + m_log.InfoFormat("[OPENSIM MAIN]: Limiting max IOCP completion threads to {0}",iocpThreads); } // set the resulting worker and IO completion thread counts back to ThreadPool if ( System.Threading.ThreadPool.SetMaxThreads(workerThreads, iocpThreads) ) { - m_log.InfoFormat("[OPENSIM MAIN]: Threadpool set to {0} worker threads and {1} IO completion threads", workerThreads, iocpThreads); + m_log.InfoFormat( + "[OPENSIM MAIN]: Threadpool set to {0} max worker threads and {1} max IOCP threads", + workerThreads, iocpThreads); } else { - m_log.Info("[OPENSIM MAIN]: Threadpool reconfiguration failed, runtime defaults still in effect."); + m_log.Warn("[OPENSIM MAIN]: Threadpool reconfiguration failed, runtime defaults still in effect."); } // Check if the system is compatible with OpenSimulator. @@ -152,17 +167,16 @@ namespace OpenSim string supported = String.Empty; if (Util.IsEnvironmentSupported(ref supported)) { - m_log.Info("Environment is compatible.\n"); + m_log.Info("[OPENSIM MAIN]: Environment is supported by OpenSimulator."); } else { - m_log.Warn("Environment is unsupported (" + supported + ")\n"); + m_log.Warn("[OPENSIM MAIN]: Environment is not supported by OpenSimulator (" + supported + ")\n"); } // Configure nIni aliases and localles Culture.SetCurrentCulture(); - // Validate that the user has the most basic configuration done // If not, offer to do the most basic configuration for them warning them along the way of the importance of // reading these files. diff --git a/OpenSim/Region/Application/ConfigurationLoader.cs b/OpenSim/Region/Application/ConfigurationLoader.cs index fc3999f7a6..52e520c41e 100644 --- a/OpenSim/Region/Application/ConfigurationLoader.cs +++ b/OpenSim/Region/Application/ConfigurationLoader.cs @@ -124,7 +124,7 @@ namespace OpenSim else { Application.iniFilePath = Path.GetFullPath( - Path.Combine(Util.configDir(), iniFileName)); + Path.Combine(Util.configDir(), iniFileName)); if (!File.Exists(Application.iniFilePath)) { @@ -139,12 +139,29 @@ namespace OpenSim } } + m_config = new OpenSimConfigSource(); + m_config.Source = new IniConfigSource(); + m_config.Source.Merge(DefaultConfig()); + + m_log.Info("[CONFIG]: Reading configuration settings"); + + for (int i = 0 ; i < sources.Count ; i++) + { + if (ReadConfig(m_config, sources[i])) + { + iniFileExists = true; + AddIncludes(m_config, sources); + } + } + + // Override distro settings with contents of inidirectory string iniDirName = startupConfig.GetString("inidirectory", "config"); string iniDirPath = Path.Combine(Util.configDir(), iniDirName); if (Directory.Exists(iniDirPath)) { - m_log.InfoFormat("Searching folder {0} for config ini files", iniDirPath); + m_log.InfoFormat("[CONFIG]: Searching folder {0} for config ini files", iniDirPath); + List overrideSources = new List(); string[] fileEntries = Directory.GetFiles(iniDirName); foreach (string filePath in fileEntries) @@ -152,33 +169,38 @@ namespace OpenSim if (Path.GetExtension(filePath).ToLower() == ".ini") { if (!sources.Contains(Path.GetFullPath(filePath))) + { + overrideSources.Add(Path.GetFullPath(filePath)); + // put it in sources too, to avoid circularity sources.Add(Path.GetFullPath(filePath)); + } } } + + + if (overrideSources.Count > 0) + { + OpenSimConfigSource overrideConfig = new OpenSimConfigSource(); + overrideConfig.Source = new IniConfigSource(); + + for (int i = 0 ; i < overrideSources.Count ; i++) + { + if (ReadConfig(overrideConfig, overrideSources[i])) + { + iniFileExists = true; + AddIncludes(overrideConfig, overrideSources); + } + } + m_config.Source.Merge(overrideConfig.Source); + } } - m_config = new OpenSimConfigSource(); - m_config.Source = new IniConfigSource(); - m_config.Source.Merge(DefaultConfig()); - - m_log.Info("[CONFIG]: Reading configuration settings"); - if (sources.Count == 0) { m_log.FatalFormat("[CONFIG]: Could not load any configuration"); Environment.Exit(1); - } - - for (int i = 0 ; i < sources.Count ; i++) - { - if (ReadConfig(sources[i])) - { - iniFileExists = true; - AddIncludes(sources); - } - } - - if (!iniFileExists) + } + else if (!iniFileExists) { m_log.FatalFormat("[CONFIG]: Could not load any configuration"); m_log.FatalFormat("[CONFIG]: Configuration exists, but there was an error loading it!"); @@ -201,9 +223,9 @@ namespace OpenSim envConfigSource.LoadEnv(); m_config.Source.Merge(envConfigSource); - m_config.Source.ExpandKeyValues(); } + m_config.Source.ExpandKeyValues(); ReadConfigSettings(); @@ -214,10 +236,10 @@ namespace OpenSim /// Adds the included files as ini configuration files /// /// List of URL strings or filename strings - private void AddIncludes(List sources) + private void AddIncludes(OpenSimConfigSource configSource, List sources) { //loop over config sources - foreach (IConfig config in m_config.Source.Configs) + foreach (IConfig config in configSource.Source.Configs) { // Look for Include-* in the key name string[] keys = config.GetKeys(); @@ -284,7 +306,7 @@ namespace OpenSim /// /// Full path to the ini /// - private bool ReadConfig(string iniPath) + private bool ReadConfig(OpenSimConfigSource configSource, string iniPath) { bool success = false; @@ -292,7 +314,7 @@ namespace OpenSim { m_log.InfoFormat("[CONFIG]: Reading configuration file {0}", Path.GetFullPath(iniPath)); - m_config.Source.Merge(new IniConfigSource(iniPath)); + configSource.Source.Merge(new IniConfigSource(iniPath)); success = true; } else @@ -305,7 +327,7 @@ namespace OpenSim { XmlReader r = XmlReader.Create(iniPath); XmlConfigSource cs = new XmlConfigSource(r); - m_config.Source.Merge(cs); + configSource.Source.Merge(cs); success = true; } @@ -337,10 +359,7 @@ namespace OpenSim config.Set("physics", "OpenDynamicsEngine"); config.Set("meshing", "Meshmerizer"); config.Set("physical_prim", true); - config.Set("see_into_this_sim_from_neighbor", true); config.Set("serverside_object_permissions", true); - config.Set("storage_plugin", "OpenSim.Data.SQLite.dll"); - config.Set("storage_connection_string", "URI=file:OpenSim.db,version=3"); config.Set("storage_prim_inventories", true); config.Set("startup_console_commands_file", String.Empty); config.Set("shutdown_console_commands_file", String.Empty); @@ -372,7 +391,6 @@ namespace OpenSim { m_configSettings.PhysicsEngine = startupConfig.GetString("physics"); m_configSettings.MeshEngineName = startupConfig.GetString("meshing"); - m_configSettings.StorageDll = startupConfig.GetString("storage_plugin"); m_configSettings.ClientstackDll = startupConfig.GetString("clientstack_plugin", "OpenSim.Region.ClientStack.LindenUDP.dll"); diff --git a/OpenSim/Region/Application/OpenSim.cs b/OpenSim/Region/Application/OpenSim.cs index a7e7d0348f..85049c9e56 100644 --- a/OpenSim/Region/Application/OpenSim.cs +++ b/OpenSim/Region/Application/OpenSim.cs @@ -86,6 +86,7 @@ namespace OpenSim IConfig startupConfig = Config.Configs["Startup"]; IConfig networkConfig = Config.Configs["Network"]; + int stpMinThreads = 2; int stpMaxThreads = 15; if (startupConfig != null) @@ -112,12 +113,13 @@ namespace OpenSim if (!String.IsNullOrEmpty(asyncCallMethodStr) && Utils.EnumTryParse(asyncCallMethodStr, out asyncCallMethod)) Util.FireAndForgetMethod = asyncCallMethod; + stpMinThreads = startupConfig.GetInt("MinPoolThreads", 15); stpMaxThreads = startupConfig.GetInt("MaxPoolThreads", 15); m_consolePrompt = startupConfig.GetString("ConsolePrompt", @"Region (\R) "); } if (Util.FireAndForgetMethod == FireAndForgetMethod.SmartThreadPool) - Util.InitThreadPool(stpMaxThreads); + Util.InitThreadPool(stpMinThreads, stpMaxThreads); m_log.Info("[OPENSIM MAIN]: Using async_call_method " + Util.FireAndForgetMethod); } @@ -170,6 +172,13 @@ namespace OpenSim if (userStatsURI != String.Empty) MainServer.Instance.AddStreamHandler(new OpenSim.UXSimStatusHandler(this)); + if (managedStatsURI != String.Empty) + { + string urlBase = String.Format("/{0}/", managedStatsURI); + MainServer.Instance.AddHTTPHandler(urlBase, StatsManager.HandleStatsRequest); + m_log.InfoFormat("[OPENSIM] Enabling remote managed stats fetch. URL = {0}", urlBase); + } + if (m_console is RemoteConsole) { if (m_consolePort == 0) @@ -226,41 +235,35 @@ namespace OpenSim "Force the update of all objects on clients", HandleForceUpdate); - m_console.Commands.AddCommand("Debug", false, "debug packet", - "debug packet [ ]", - "Turn on packet debugging", - "If level > 255 then all incoming and outgoing packets are logged.\n" - + "If level <= 255 then incoming AgentUpdate and outgoing SimStats and SimulatorViewerTimeMessage packets are not logged.\n" - + "If level <= 200 then incoming RequestImage and outgoing ImagePacket, ImageData, LayerData and CoarseLocationUpdate packets are not logged.\n" - + "If level <= 100 then incoming ViewerEffect and AgentAnimation and outgoing ViewerEffect and AvatarAnimation packets are not logged.\n" - + "If level <= 50 then outgoing ImprovedTerseObjectUpdate packets are not logged.\n" - + "If level <= 0 then no packets are logged.\n" - + "If an avatar name is given then only packets from that avatar are logged", - Debug); - m_console.Commands.AddCommand("General", false, "change region", "change region ", - "Change current console region", ChangeSelectedRegion); + "Change current console region", + ChangeSelectedRegion); m_console.Commands.AddCommand("Archiving", false, "save xml", "save xml", - "Save a region's data in XML format", SaveXml); + "Save a region's data in XML format", + SaveXml); m_console.Commands.AddCommand("Archiving", false, "save xml2", "save xml2", - "Save a region's data in XML2 format", SaveXml2); + "Save a region's data in XML2 format", + SaveXml2); m_console.Commands.AddCommand("Archiving", false, "load xml", "load xml [-newIDs [ ]]", - "Load a region's data from XML format", LoadXml); + "Load a region's data from XML format", + LoadXml); m_console.Commands.AddCommand("Archiving", false, "load xml2", "load xml2", - "Load a region's data from XML2 format", LoadXml2); + "Load a region's data from XML2 format", + LoadXml2); m_console.Commands.AddCommand("Archiving", false, "save prims xml2", "save prims xml2 [ ]", - "Save named prim to XML2", SavePrimsXml2); + "Save named prim to XML2", + SavePrimsXml2); m_console.Commands.AddCommand("Archiving", false, "load oar", "load oar [--merge] [--skip-assets] []", @@ -290,7 +293,23 @@ namespace OpenSim m_console.Commands.AddCommand("Objects", false, "edit scale", "edit scale ", - "Change the scale of a named prim", HandleEditScale); + "Change the scale of a named prim", + HandleEditScale); + + m_console.Commands.AddCommand("Objects", false, "rotate scene", + "rotate scene [centerX, centerY]", + "Rotates all scene objects around centerX, centerY (defailt 128, 128) (please back up your region before using)", + HandleRotateScene); + + m_console.Commands.AddCommand("Objects", false, "scale scene", + "scale scene ", + "Scales the scene objects (please back up your region before using)", + HandleScaleScene); + + m_console.Commands.AddCommand("Objects", false, "translate scene", + "translate scene xOffset yOffset zOffset", + "translates the scene objects (please back up your region before using)", + HandleTranslateScene); m_console.Commands.AddCommand("Users", false, "kick user", "kick user [--force] [message]", @@ -308,31 +327,38 @@ namespace OpenSim m_console.Commands.AddCommand("Comms", false, "show connections", "show connections", - "Show connection data", HandleShow); + "Show connection data", + HandleShow); m_console.Commands.AddCommand("Comms", false, "show circuits", "show circuits", - "Show agent circuit data", HandleShow); + "Show agent circuit data", + HandleShow); m_console.Commands.AddCommand("Comms", false, "show pending-objects", "show pending-objects", - "Show # of objects on the pending queues of all scene viewers", HandleShow); + "Show # of objects on the pending queues of all scene viewers", + HandleShow); m_console.Commands.AddCommand("General", false, "show modules", "show modules", - "Show module data", HandleShow); + "Show module data", + HandleShow); m_console.Commands.AddCommand("Regions", false, "show regions", "show regions", - "Show region data", HandleShow); + "Show region data", + HandleShow); m_console.Commands.AddCommand("Regions", false, "show ratings", "show ratings", - "Show rating data", HandleShow); + "Show rating data", + HandleShow); m_console.Commands.AddCommand("Objects", false, "backup", "backup", - "Persist currently unsaved object changes immediately instead of waiting for the normal persistence call.", RunCommand); + "Persist currently unsaved object changes immediately instead of waiting for the normal persistence call.", + RunCommand); m_console.Commands.AddCommand("Regions", false, "create region", "create region [\"region name\"] ", @@ -345,34 +371,26 @@ namespace OpenSim m_console.Commands.AddCommand("Regions", false, "restart", "restart", - "Restart all sims in this instance", RunCommand); + "Restart all sims in this instance", + RunCommand); m_console.Commands.AddCommand("General", false, "command-script", "command-script