mirror of
https://github.com/opensim/opensim.git
synced 2026-05-14 10:45:40 +08:00
* First pass at applying the using(){} pattern to IDisposable objects. Always use the using pattern on IDisposable objects whenever possible, do not manually call .Close() or .Dispose() unless there is no other way to write the code. This pass mostly covers OpenSim.Data.MySQL, and should have no functional change (tests still pass)
397 lines
15 KiB
C#
397 lines
15 KiB
C#
/*
|
|
* 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 MySql.Data.MySqlClient;
|
|
using OpenMetaverse;
|
|
using OpenSim.Framework;
|
|
using OpenSim.Region.Framework.Interfaces;
|
|
|
|
namespace OpenSim.Data.MySQL
|
|
{
|
|
public class MySQLEstateStore : IEstateDataStore
|
|
{
|
|
private static readonly ILog m_log =
|
|
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
|
|
|
private const string m_waitTimeoutSelect = "select @@wait_timeout";
|
|
|
|
private MySqlConnection m_connection;
|
|
private string m_connectionString;
|
|
private long m_waitTimeout;
|
|
private long m_waitTimeoutLeeway = 60 * TimeSpan.TicksPerSecond;
|
|
private long m_lastConnectionUse;
|
|
|
|
private FieldInfo[] m_Fields;
|
|
private Dictionary<string, FieldInfo> m_FieldMap =
|
|
new Dictionary<string, FieldInfo>();
|
|
|
|
public void Initialise(string connectionString)
|
|
{
|
|
m_connectionString = connectionString;
|
|
|
|
try
|
|
{
|
|
m_log.Info("[REGION DB]: MySql - connecting: " + Util.GetDisplayConnectionString(m_connectionString));
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
m_log.Debug("Exception: password not found in connection string\n" + e.ToString());
|
|
}
|
|
|
|
m_connection = new MySqlConnection(m_connectionString);
|
|
m_connection.Open();
|
|
|
|
GetWaitTimeout();
|
|
|
|
Assembly assem = GetType().Assembly;
|
|
Migration m = new Migration(m_connection, assem, "EstateStore");
|
|
m.Update();
|
|
|
|
Type t = typeof(EstateSettings);
|
|
m_Fields = t.GetFields(BindingFlags.NonPublic |
|
|
BindingFlags.Instance |
|
|
BindingFlags.DeclaredOnly);
|
|
|
|
foreach (FieldInfo f in m_Fields)
|
|
{
|
|
if (f.Name.Substring(0, 2) == "m_")
|
|
m_FieldMap[f.Name.Substring(2)] = f;
|
|
}
|
|
}
|
|
|
|
private string[] FieldList
|
|
{
|
|
get { return new List<string>(m_FieldMap.Keys).ToArray(); }
|
|
}
|
|
|
|
protected void GetWaitTimeout()
|
|
{
|
|
using (MySqlCommand cmd = new MySqlCommand(m_waitTimeoutSelect, m_connection))
|
|
{
|
|
using (MySqlDataReader dbReader = cmd.ExecuteReader(CommandBehavior.SingleRow))
|
|
{
|
|
if (dbReader.Read())
|
|
{
|
|
m_waitTimeout
|
|
= Convert.ToInt32(dbReader["@@wait_timeout"]) *
|
|
TimeSpan.TicksPerSecond + m_waitTimeoutLeeway;
|
|
}
|
|
}
|
|
}
|
|
|
|
m_lastConnectionUse = DateTime.Now.Ticks;
|
|
|
|
m_log.DebugFormat(
|
|
"[REGION DB]: Connection wait timeout {0} seconds",
|
|
m_waitTimeout / TimeSpan.TicksPerSecond);
|
|
}
|
|
|
|
protected void CheckConnection()
|
|
{
|
|
long timeNow = DateTime.Now.Ticks;
|
|
if (timeNow - m_lastConnectionUse > m_waitTimeout ||
|
|
m_connection.State != ConnectionState.Open)
|
|
{
|
|
m_log.DebugFormat("[REGION DB]: Database connection has gone away - reconnecting");
|
|
|
|
lock (m_connection)
|
|
{
|
|
m_connection.Close();
|
|
m_connection = new MySqlConnection(m_connectionString);
|
|
m_connection.Open();
|
|
}
|
|
}
|
|
|
|
m_lastConnectionUse = timeNow;
|
|
}
|
|
|
|
public EstateSettings LoadEstateSettings(UUID regionID)
|
|
{
|
|
EstateSettings es = new EstateSettings();
|
|
es.OnSave += StoreEstateSettings;
|
|
|
|
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";
|
|
|
|
CheckConnection();
|
|
|
|
bool migration = true;
|
|
|
|
using (MySqlCommand cmd = m_connection.CreateCommand())
|
|
{
|
|
cmd.CommandText = sql;
|
|
cmd.Parameters.AddWithValue("?RegionID", regionID.ToString());
|
|
|
|
using (IDataReader r = cmd.ExecuteReader())
|
|
{
|
|
if (r.Read())
|
|
{
|
|
migration = false;
|
|
|
|
foreach (string name in FieldList)
|
|
{
|
|
if (m_FieldMap[name].GetValue(es) is bool)
|
|
{
|
|
int v = Convert.ToInt32(r[name]);
|
|
if (v != 0)
|
|
m_FieldMap[name].SetValue(es, true);
|
|
else
|
|
m_FieldMap[name].SetValue(es, false);
|
|
}
|
|
else if (m_FieldMap[name].GetValue(es) is UUID)
|
|
{
|
|
UUID uuid = UUID.Zero;
|
|
|
|
UUID.TryParse(r[name].ToString(), out uuid);
|
|
m_FieldMap[name].SetValue(es, uuid);
|
|
}
|
|
else
|
|
{
|
|
m_FieldMap[name].SetValue(es, r[name]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (migration)
|
|
{
|
|
// Migration case
|
|
List<string> names = new List<string>(FieldList);
|
|
|
|
names.Remove("EstateID");
|
|
|
|
sql = "insert into estate_settings (" + String.Join(",", names.ToArray()) + ") values ( ?" + String.Join(", ?", names.ToArray()) + ")";
|
|
|
|
using (MySqlCommand cmd = m_connection.CreateCommand())
|
|
{
|
|
cmd.CommandText = sql;
|
|
cmd.Parameters.Clear();
|
|
|
|
foreach (string name in FieldList)
|
|
{
|
|
if (m_FieldMap[name].GetValue(es) is bool)
|
|
{
|
|
if ((bool)m_FieldMap[name].GetValue(es))
|
|
cmd.Parameters.AddWithValue("?" + name, "1");
|
|
else
|
|
cmd.Parameters.AddWithValue("?" + name, "0");
|
|
}
|
|
else
|
|
{
|
|
cmd.Parameters.AddWithValue("?" + name, m_FieldMap[name].GetValue(es).ToString());
|
|
}
|
|
}
|
|
|
|
cmd.ExecuteNonQuery();
|
|
|
|
cmd.CommandText = "select LAST_INSERT_ID() as id";
|
|
cmd.Parameters.Clear();
|
|
|
|
using (IDataReader r = cmd.ExecuteReader())
|
|
{
|
|
r.Read();
|
|
es.EstateID = Convert.ToUInt32(r["id"]);
|
|
}
|
|
|
|
cmd.CommandText = "insert into estate_map values (?RegionID, ?EstateID)";
|
|
cmd.Parameters.AddWithValue("?RegionID", regionID.ToString());
|
|
cmd.Parameters.AddWithValue("?EstateID", es.EstateID.ToString());
|
|
|
|
// This will throw on dupe key
|
|
try { cmd.ExecuteNonQuery(); }
|
|
catch (Exception) { }
|
|
|
|
// Munge and transfer the ban list
|
|
cmd.Parameters.Clear();
|
|
cmd.CommandText = "insert into estateban select " + es.EstateID.ToString() + ", bannedUUID, bannedIp, bannedIpHostMask, '' from regionban where regionban.regionUUID = ?UUID";
|
|
cmd.Parameters.AddWithValue("?UUID", regionID.ToString());
|
|
|
|
try { cmd.ExecuteNonQuery(); }
|
|
catch (Exception) { }
|
|
|
|
es.Save();
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
public void StoreEstateSettings(EstateSettings es)
|
|
{
|
|
string sql = "replace into estate_settings (" + String.Join(",", FieldList) + ") values ( ?" + String.Join(", ?", FieldList) + ")";
|
|
|
|
CheckConnection();
|
|
|
|
using (MySqlCommand cmd = m_connection.CreateCommand())
|
|
{
|
|
cmd.CommandText = sql;
|
|
|
|
foreach (string name in FieldList)
|
|
{
|
|
if (m_FieldMap[name].GetValue(es) is bool)
|
|
{
|
|
if ((bool)m_FieldMap[name].GetValue(es))
|
|
cmd.Parameters.AddWithValue("?" + name, "1");
|
|
else
|
|
cmd.Parameters.AddWithValue("?" + name, "0");
|
|
}
|
|
else
|
|
{
|
|
cmd.Parameters.AddWithValue("?" + name, m_FieldMap[name].GetValue(es).ToString());
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
private void LoadBanList(EstateSettings es)
|
|
{
|
|
es.ClearBans();
|
|
|
|
CheckConnection();
|
|
|
|
using (MySqlCommand cmd = m_connection.CreateCommand())
|
|
{
|
|
cmd.CommandText = "select bannedUUID from estateban where EstateID = ?EstateID";
|
|
cmd.Parameters.AddWithValue("?EstateID", es.EstateID);
|
|
|
|
using (IDataReader r = cmd.ExecuteReader())
|
|
{
|
|
while (r.Read())
|
|
{
|
|
EstateBan eb = new EstateBan();
|
|
|
|
UUID uuid = new UUID();
|
|
UUID.TryParse(r["bannedUUID"].ToString(), out uuid);
|
|
|
|
eb.BannedUserID = uuid;
|
|
eb.BannedHostAddress = "0.0.0.0";
|
|
eb.BannedHostIPMask = "0.0.0.0";
|
|
es.AddBan(eb);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void SaveBanList(EstateSettings es)
|
|
{
|
|
CheckConnection();
|
|
|
|
using (MySqlCommand cmd = m_connection.CreateCommand())
|
|
{
|
|
cmd.CommandText = "delete from estateban where EstateID = ?EstateID";
|
|
cmd.Parameters.AddWithValue("?EstateID", es.EstateID.ToString());
|
|
|
|
cmd.ExecuteNonQuery();
|
|
|
|
cmd.Parameters.Clear();
|
|
|
|
cmd.CommandText = "insert into estateban (EstateID, bannedUUID, bannedIp, bannedIpHostMask, bannedNameMask) values ( ?EstateID, ?bannedUUID, '', '', '' )";
|
|
|
|
foreach (EstateBan b in es.EstateBans)
|
|
{
|
|
cmd.Parameters.AddWithValue("?EstateID", es.EstateID.ToString());
|
|
cmd.Parameters.AddWithValue("?bannedUUID", b.BannedUserID.ToString());
|
|
|
|
cmd.ExecuteNonQuery();
|
|
cmd.Parameters.Clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
void SaveUUIDList(uint EstateID, string table, UUID[] data)
|
|
{
|
|
CheckConnection();
|
|
|
|
using (MySqlCommand cmd = m_connection.CreateCommand())
|
|
{
|
|
cmd.CommandText = "delete from " + table + " where EstateID = ?EstateID";
|
|
cmd.Parameters.AddWithValue("?EstateID", EstateID.ToString());
|
|
|
|
cmd.ExecuteNonQuery();
|
|
|
|
cmd.Parameters.Clear();
|
|
|
|
cmd.CommandText = "insert into " + table + " (EstateID, uuid) values ( ?EstateID, ?uuid )";
|
|
|
|
foreach (UUID uuid in data)
|
|
{
|
|
cmd.Parameters.AddWithValue("?EstateID", EstateID.ToString());
|
|
cmd.Parameters.AddWithValue("?uuid", uuid.ToString());
|
|
|
|
cmd.ExecuteNonQuery();
|
|
cmd.Parameters.Clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
UUID[] LoadUUIDList(uint EstateID, string table)
|
|
{
|
|
List<UUID> uuids = new List<UUID>();
|
|
|
|
CheckConnection();
|
|
|
|
using (MySqlCommand cmd = m_connection.CreateCommand())
|
|
{
|
|
cmd.CommandText = "select uuid from " + table + " where EstateID = ?EstateID";
|
|
cmd.Parameters.AddWithValue("?EstateID", EstateID);
|
|
|
|
using (IDataReader r = cmd.ExecuteReader())
|
|
{
|
|
while (r.Read())
|
|
{
|
|
// EstateBan eb = new EstateBan();
|
|
|
|
UUID uuid = new UUID();
|
|
UUID.TryParse(r["uuid"].ToString(), out uuid);
|
|
|
|
uuids.Add(uuid);
|
|
}
|
|
}
|
|
}
|
|
|
|
return uuids.ToArray();
|
|
}
|
|
}
|
|
}
|