/*
* 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 MySql.Data.MySqlClient;
using OpenMetaverse;
using OpenSim.Framework;
namespace OpenSim.Data.MySQL
{
///
/// A MySQL Interface for the Grid Server
///
public class MySQLGridData : GridDataBase
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private MySQLManager m_database;
private object m_dbLock = new object();
private string m_connectionString;
override public void Initialise()
{
m_log.Info("[MySQLGridData]: " + Name + " cannot be default-initialized!");
throw new PluginNotInitialisedException (Name);
}
///
/// Initialises Grid interface
///
///
/// - Loads and initialises the MySQL storage plugin
/// - Warns and uses the obsolete mysql_connection.ini if connect string is empty.
/// - Check for migration
///
///
///
/// connect string.
override public void Initialise(string connect)
{
m_connectionString = connect;
m_database = new MySQLManager(connect);
// This actually does the roll forward assembly stuff
Assembly assem = GetType().Assembly;
using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
{
Migration m = new Migration(dbcon, assem, "GridStore");
m.Update();
}
}
///
/// Shuts down the grid interface
///
override public void Dispose()
{
}
///
/// Returns the plugin name
///
/// Plugin name
override public string Name
{
get { return "MySql OpenGridData"; }
}
///
/// Returns the plugin version
///
/// Plugin version
override public string Version
{
get { return "0.1"; }
}
///
/// Returns all the specified region profiles within coordates -- coordinates are inclusive
///
/// Minimum X coordinate
/// Minimum Y coordinate
/// Maximum X coordinate
/// Maximum Y coordinate
/// Array of sim profiles
override public RegionProfileData[] GetProfilesInRange(uint xmin, uint ymin, uint xmax, uint ymax)
{
try
{
Dictionary param = new Dictionary();
param["?xmin"] = xmin.ToString();
param["?ymin"] = ymin.ToString();
param["?xmax"] = xmax.ToString();
param["?ymax"] = ymax.ToString();
using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
{
dbcon.Open();
using (IDbCommand result = m_database.Query(dbcon,
"SELECT * FROM regions WHERE locX >= ?xmin AND locX <= ?xmax AND locY >= ?ymin AND locY <= ?ymax",
param))
{
using (IDataReader reader = result.ExecuteReader())
{
RegionProfileData row;
List rows = new List();
while ((row = m_database.readSimRow(reader)) != null)
rows.Add(row);
return rows.ToArray();
}
}
}
}
catch (Exception e)
{
m_log.Error(e.Message, e);
return null;
}
}
///
/// Returns up to maxNum profiles of regions that have a name starting with namePrefix
///
/// The name to match against
/// Maximum number of profiles to return
/// A list of sim profiles
override public List GetRegionsByName(string namePrefix, uint maxNum)
{
try
{
Dictionary param = new Dictionary();
param["?name"] = namePrefix + "%";
using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
{
dbcon.Open();
using (IDbCommand result = m_database.Query(dbcon,
"SELECT * FROM regions WHERE regionName LIKE ?name",
param))
{
using (IDataReader reader = result.ExecuteReader())
{
RegionProfileData row;
List rows = new List();
while (rows.Count < maxNum && (row = m_database.readSimRow(reader)) != null)
rows.Add(row);
return rows;
}
}
}
}
catch (Exception e)
{
m_log.Error(e.Message, e);
return null;
}
}
///
/// Returns a sim profile from it's location
///
/// Region location handle
/// Sim profile
override public RegionProfileData GetProfileByHandle(ulong handle)
{
try
{
Dictionary param = new Dictionary();
param["?handle"] = handle.ToString();
using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
{
dbcon.Open();
using (IDbCommand result = m_database.Query(dbcon, "SELECT * FROM regions WHERE regionHandle = ?handle", param))
{
using (IDataReader reader = result.ExecuteReader())
{
RegionProfileData row = m_database.readSimRow(reader);
return row;
}
}
}
}
catch (Exception e)
{
m_log.Error(e.Message, e);
return null;
}
}
///
/// Returns a sim profile from it's UUID
///
/// The region UUID
/// The sim profile
override public RegionProfileData GetProfileByUUID(UUID uuid)
{
try
{
Dictionary param = new Dictionary();
param["?uuid"] = uuid.ToString();
using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
{
dbcon.Open();
using (IDbCommand result = m_database.Query(dbcon, "SELECT * FROM regions WHERE uuid = ?uuid", param))
{
using (IDataReader reader = result.ExecuteReader())
{
RegionProfileData row = m_database.readSimRow(reader);
return row;
}
}
}
}
catch (Exception e)
{
m_log.Error(e.Message, e);
return null;
}
}
///
/// Returns a sim profile from it's Region name string
///
/// The sim profile
override public RegionProfileData GetProfileByString(string regionName)
{
if (regionName.Length > 2)
{
try
{
Dictionary param = new Dictionary();
// Add % because this is a like query.
param["?regionName"] = regionName + "%";
using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
{
dbcon.Open();
// Order by statement will return shorter matches first. Only returns one record or no record.
using (IDbCommand result = m_database.Query(dbcon,
"SELECT * FROM regions WHERE regionName like ?regionName order by LENGTH(regionName) asc LIMIT 1",
param))
{
using (IDataReader reader = result.ExecuteReader())
{
RegionProfileData row = m_database.readSimRow(reader);
return row;
}
}
}
}
catch (Exception e)
{
m_log.Error(e.Message, e);
return null;
}
}
m_log.Error("[GRID DB]: Searched for a Region Name shorter then 3 characters");
return null;
}
///
/// Adds a new profile to the database
///
/// The profile to add
/// Successful?
override public DataResponse StoreProfile(RegionProfileData profile)
{
try
{
if (m_database.insertRegion(profile))
return DataResponse.RESPONSE_OK;
else
return DataResponse.RESPONSE_ERROR;
}
catch
{
return DataResponse.RESPONSE_ERROR;
}
}
///
/// Deletes a sim profile from the database
///
/// the sim UUID
/// Successful?
//public DataResponse DeleteProfile(RegionProfileData profile)
override public DataResponse DeleteProfile(string uuid)
{
try
{
if (m_database.deleteRegion(uuid))
return DataResponse.RESPONSE_OK;
else
return DataResponse.RESPONSE_ERROR;
}
catch
{
return DataResponse.RESPONSE_ERROR;
}
}
///
/// DEPRECATED. Attempts to authenticate a region by comparing a shared secret.
///
/// The UUID of the challenger
/// The attempted regionHandle of the challenger
/// The secret
/// Whether the secret and regionhandle match the database entry for UUID
override public bool AuthenticateSim(UUID uuid, ulong handle, string authkey)
{
bool throwHissyFit = false; // Should be true by 1.0
if (throwHissyFit)
throw new Exception("CRYPTOWEAK AUTHENTICATE: Refusing to authenticate due to replay potential.");
RegionProfileData data = GetProfileByUUID(uuid);
return (handle == data.regionHandle && authkey == data.regionSecret);
}
///
/// NOT YET FUNCTIONAL. Provides a cryptographic authentication of a region
///
/// This requires a security audit.
///
///
///
///
///
public bool AuthenticateSim(UUID uuid, ulong handle, string authhash, string challenge)
{
// SHA512Managed HashProvider = new SHA512Managed();
// Encoding TextProvider = new UTF8Encoding();
// byte[] stream = TextProvider.GetBytes(uuid.ToString() + ":" + handle.ToString() + ":" + challenge);
// byte[] hash = HashProvider.ComputeHash(stream);
return false;
}
///
/// Adds a location reservation
///
/// x coordinate
/// y coordinate
///
override public ReservationData GetReservationAtPoint(uint x, uint y)
{
try
{
Dictionary param = new Dictionary();
param["?x"] = x.ToString();
param["?y"] = y.ToString();
using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
{
dbcon.Open();
using (IDbCommand result = m_database.Query(dbcon,
"SELECT * FROM reservations WHERE resXMin <= ?x AND resXMax >= ?x AND resYMin <= ?y AND resYMax >= ?y",
param))
{
using (IDataReader reader = result.ExecuteReader())
{
ReservationData row = m_database.readReservationRow(reader);
return row;
}
}
}
}
catch (Exception e)
{
m_log.Error(e.Message, e);
return null;
}
}
}
}