MySQLGridData.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. /*
  2. * Copyright (c) Contributors, http://opensimulator.org/
  3. * See CONTRIBUTORS.TXT for a full list of copyright holders.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * * Neither the name of the OpenSim Project nor the
  13. * names of its contributors may be used to endorse or promote products
  14. * derived from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
  17. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
  20. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. using System;
  28. using System.Collections.Generic;
  29. using System.Data;
  30. using System.Reflection;
  31. using System.Security.Cryptography;
  32. using System.Text;
  33. using libsecondlife;
  34. using log4net;
  35. using Mono.Addins;
  36. using OpenSim.Framework;
  37. [assembly : Addin]
  38. [assembly : AddinDependency("OpenSim.Data", "0.5")]
  39. namespace OpenSim.Data.MySQL
  40. {
  41. /// <summary>
  42. /// A MySQL Interface for the Grid Server
  43. /// </summary>
  44. [Extension("/OpenSim/GridDataStore")]
  45. public class MySQLGridData : GridDataBase
  46. {
  47. private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  48. /// <summary>
  49. /// MySQL Database Manager
  50. /// </summary>
  51. private MySQLManager database;
  52. override public void Initialise()
  53. {
  54. m_log.Info("[MySQLLogData]: " + Name + " cannot be default-initialized!");
  55. throw new PluginNotInitialisedException (Name);
  56. }
  57. /// <summary>
  58. /// <para>Initialises Grid interface</para>
  59. /// <para>
  60. /// <list type="bullet">
  61. /// <item>Loads and initialises the MySQL storage plugin</item>
  62. /// <item>Warns and uses the obsolete mysql_connection.ini if connect string is empty.</item>
  63. /// <item>Check for migration</item>
  64. /// </list>
  65. /// </para>
  66. /// </summary>
  67. /// <param name="connect">connect string.</param>
  68. override public void Initialise(string connect)
  69. {
  70. if (connect != String.Empty)
  71. {
  72. database = new MySQLManager(connect);
  73. }
  74. else
  75. {
  76. m_log.Warn("Using deprecated mysql_connection.ini. Please update database_connect in GridServer_Config.xml and we'll use that instead");
  77. IniFile GridDataMySqlFile = new IniFile("mysql_connection.ini");
  78. string settingHostname = GridDataMySqlFile.ParseFileReadValue("hostname");
  79. string settingDatabase = GridDataMySqlFile.ParseFileReadValue("database");
  80. string settingUsername = GridDataMySqlFile.ParseFileReadValue("username");
  81. string settingPassword = GridDataMySqlFile.ParseFileReadValue("password");
  82. string settingPooling = GridDataMySqlFile.ParseFileReadValue("pooling");
  83. string settingPort = GridDataMySqlFile.ParseFileReadValue("port");
  84. database = new MySQLManager(settingHostname, settingDatabase, settingUsername, settingPassword,
  85. settingPooling, settingPort);
  86. }
  87. // This actually does the roll forward assembly stuff
  88. Assembly assem = GetType().Assembly;
  89. Migration m = new Migration(database.Connection, assem, "GridStore");
  90. // TODO: After rev 6000, remove this. People should have
  91. // been rolled onto the new migration code by then.
  92. TestTables(m);
  93. m.Update();
  94. }
  95. #region Test and initialization code
  96. /// <summary>
  97. /// Ensure that the user related tables exists and are at the latest version
  98. /// </summary>
  99. private void TestTables(Migration m)
  100. {
  101. // we already have migrations, get out of here
  102. if (m.Version > 0)
  103. return;
  104. Dictionary<string, string> tableList = new Dictionary<string, string>();
  105. tableList["regions"] = null;
  106. database.GetTableVersion(tableList);
  107. UpgradeRegionsTable(tableList["regions"]);
  108. // we have tables, but not a migration model yet
  109. if (m.Version == 0)
  110. m.Version = 1;
  111. }
  112. /// <summary>
  113. /// Create or upgrade the table if necessary
  114. /// </summary>
  115. /// <param name="oldVersion">A null indicates that the table does not
  116. /// currently exist</param>
  117. private void UpgradeRegionsTable(string oldVersion)
  118. {
  119. // null as the version, indicates that the table didn't exist
  120. if (oldVersion == null)
  121. {
  122. database.ExecuteResourceSql("CreateRegionsTable.sql");
  123. return;
  124. }
  125. if (oldVersion.Contains("Rev. 1"))
  126. {
  127. database.ExecuteResourceSql("UpgradeRegionsTableToVersion2.sql");
  128. return;
  129. }
  130. if (oldVersion.Contains("Rev. 2"))
  131. {
  132. database.ExecuteResourceSql("UpgradeRegionsTableToVersion3.sql");
  133. return;
  134. }
  135. }
  136. #endregion
  137. /// <summary>
  138. /// Shuts down the grid interface
  139. /// </summary>
  140. override public void Dispose()
  141. {
  142. database.Close();
  143. }
  144. /// <summary>
  145. /// Returns the plugin name
  146. /// </summary>
  147. /// <returns>Plugin name</returns>
  148. override public string Name
  149. {
  150. get { return "MySql OpenGridData"; }
  151. }
  152. /// <summary>
  153. /// Returns the plugin version
  154. /// </summary>
  155. /// <returns>Plugin version</returns>
  156. override public string Version
  157. {
  158. get { return "0.1"; }
  159. }
  160. /// <summary>
  161. /// Returns all the specified region profiles within coordates -- coordinates are inclusive
  162. /// </summary>
  163. /// <param name="xmin">Minimum X coordinate</param>
  164. /// <param name="ymin">Minimum Y coordinate</param>
  165. /// <param name="xmax">Maximum X coordinate</param>
  166. /// <param name="ymax">Maximum Y coordinate</param>
  167. /// <returns>Array of sim profiles</returns>
  168. override public RegionProfileData[] GetProfilesInRange(uint xmin, uint ymin, uint xmax, uint ymax)
  169. {
  170. try
  171. {
  172. lock (database)
  173. {
  174. Dictionary<string, string> param = new Dictionary<string, string>();
  175. param["?xmin"] = xmin.ToString();
  176. param["?ymin"] = ymin.ToString();
  177. param["?xmax"] = xmax.ToString();
  178. param["?ymax"] = ymax.ToString();
  179. IDbCommand result =
  180. database.Query(
  181. "SELECT * FROM regions WHERE locX >= ?xmin AND locX <= ?xmax AND locY >= ?ymin AND locY <= ?ymax",
  182. param);
  183. IDataReader reader = result.ExecuteReader();
  184. RegionProfileData row;
  185. List<RegionProfileData> rows = new List<RegionProfileData>();
  186. while ((row = database.readSimRow(reader)) != null)
  187. {
  188. rows.Add(row);
  189. }
  190. reader.Close();
  191. result.Dispose();
  192. return rows.ToArray();
  193. }
  194. }
  195. catch (Exception e)
  196. {
  197. database.Reconnect();
  198. m_log.Error(e.ToString());
  199. return null;
  200. }
  201. }
  202. /// <summary>
  203. /// Returns a sim profile from it's location
  204. /// </summary>
  205. /// <param name="handle">Region location handle</param>
  206. /// <returns>Sim profile</returns>
  207. override public RegionProfileData GetProfileByHandle(ulong handle)
  208. {
  209. try
  210. {
  211. lock (database)
  212. {
  213. Dictionary<string, string> param = new Dictionary<string, string>();
  214. param["?handle"] = handle.ToString();
  215. IDbCommand result = database.Query("SELECT * FROM regions WHERE regionHandle = ?handle", param);
  216. IDataReader reader = result.ExecuteReader();
  217. RegionProfileData row = database.readSimRow(reader);
  218. reader.Close();
  219. result.Dispose();
  220. return row;
  221. }
  222. }
  223. catch (Exception e)
  224. {
  225. database.Reconnect();
  226. m_log.Error(e.ToString());
  227. return null;
  228. }
  229. }
  230. /// <summary>
  231. /// Returns a sim profile from it's UUID
  232. /// </summary>
  233. /// <param name="uuid">The region UUID</param>
  234. /// <returns>The sim profile</returns>
  235. override public RegionProfileData GetProfileByLLUUID(LLUUID uuid)
  236. {
  237. try
  238. {
  239. lock (database)
  240. {
  241. Dictionary<string, string> param = new Dictionary<string, string>();
  242. param["?uuid"] = uuid.ToString();
  243. IDbCommand result = database.Query("SELECT * FROM regions WHERE uuid = ?uuid", param);
  244. IDataReader reader = result.ExecuteReader();
  245. RegionProfileData row = database.readSimRow(reader);
  246. reader.Close();
  247. result.Dispose();
  248. return row;
  249. }
  250. }
  251. catch (Exception e)
  252. {
  253. database.Reconnect();
  254. m_log.Error(e.ToString());
  255. return null;
  256. }
  257. }
  258. /// <summary>
  259. /// Returns a sim profile from it's Region name string
  260. /// </summary>
  261. /// <param name="uuid">The region name search query</param>
  262. /// <returns>The sim profile</returns>
  263. override public RegionProfileData GetProfileByString(string regionName)
  264. {
  265. if (regionName.Length > 2)
  266. {
  267. try
  268. {
  269. lock (database)
  270. {
  271. Dictionary<string, string> param = new Dictionary<string, string>();
  272. // Add % because this is a like query.
  273. param["?regionName"] = regionName + "%";
  274. // Order by statement will return shorter matches first. Only returns one record or no record.
  275. IDbCommand result = database.Query("SELECT * FROM regions WHERE regionName like ?regionName order by LENGTH(regionName) asc LIMIT 1", param);
  276. IDataReader reader = result.ExecuteReader();
  277. RegionProfileData row = database.readSimRow(reader);
  278. reader.Close();
  279. result.Dispose();
  280. return row;
  281. }
  282. }
  283. catch (Exception e)
  284. {
  285. database.Reconnect();
  286. m_log.Error(e.ToString());
  287. return null;
  288. }
  289. }
  290. else
  291. {
  292. m_log.Error("[GRID DB]: Searched for a Region Name shorter then 3 characters");
  293. return null;
  294. }
  295. }
  296. /// <summary>
  297. /// Adds a new profile to the database
  298. /// </summary>
  299. /// <param name="profile">The profile to add</param>
  300. /// <returns>Successful?</returns>
  301. override public DataResponse AddProfile(RegionProfileData profile)
  302. {
  303. lock (database)
  304. {
  305. if (database.insertRegion(profile))
  306. {
  307. return DataResponse.RESPONSE_OK;
  308. }
  309. else
  310. {
  311. return DataResponse.RESPONSE_ERROR;
  312. }
  313. }
  314. }
  315. /// <summary>
  316. /// Update a sim profile
  317. /// </summary>
  318. /// <param name="profile">The profile to update</param>
  319. /// <returns>Sucessful?</returns>
  320. /// <remarks>Same as AddProfile</remarks>
  321. override public DataResponse UpdateProfile(RegionProfileData profile)
  322. {
  323. return AddProfile(profile);
  324. }
  325. /// <summary>
  326. /// Deletes a sim profile from the database
  327. /// </summary>
  328. /// <param name="uuid">the sim UUID</param>
  329. /// <returns>Successful?</returns>
  330. //public DataResponse DeleteProfile(RegionProfileData profile)
  331. public DataResponse DeleteProfile(string uuid)
  332. {
  333. lock (database)
  334. {
  335. if (database.deleteRegion(uuid))
  336. {
  337. return DataResponse.RESPONSE_OK;
  338. }
  339. else
  340. {
  341. return DataResponse.RESPONSE_ERROR;
  342. }
  343. }
  344. }
  345. /// <summary>
  346. /// DEPRECATED. Attempts to authenticate a region by comparing a shared secret.
  347. /// </summary>
  348. /// <param name="uuid">The UUID of the challenger</param>
  349. /// <param name="handle">The attempted regionHandle of the challenger</param>
  350. /// <param name="authkey">The secret</param>
  351. /// <returns>Whether the secret and regionhandle match the database entry for UUID</returns>
  352. override public bool AuthenticateSim(LLUUID uuid, ulong handle, string authkey)
  353. {
  354. bool throwHissyFit = false; // Should be true by 1.0
  355. if (throwHissyFit)
  356. throw new Exception("CRYPTOWEAK AUTHENTICATE: Refusing to authenticate due to replay potential.");
  357. RegionProfileData data = GetProfileByLLUUID(uuid);
  358. return (handle == data.regionHandle && authkey == data.regionSecret);
  359. }
  360. /// <summary>
  361. /// NOT YET FUNCTIONAL. Provides a cryptographic authentication of a region
  362. /// </summary>
  363. /// <remarks>This requires a security audit.</remarks>
  364. /// <param name="uuid"></param>
  365. /// <param name="handle"></param>
  366. /// <param name="authhash"></param>
  367. /// <param name="challenge"></param>
  368. /// <returns></returns>
  369. public bool AuthenticateSim(LLUUID uuid, ulong handle, string authhash, string challenge)
  370. {
  371. // SHA512Managed HashProvider = new SHA512Managed();
  372. // Encoding TextProvider = new UTF8Encoding();
  373. // byte[] stream = TextProvider.GetBytes(uuid.ToString() + ":" + handle.ToString() + ":" + challenge);
  374. // byte[] hash = HashProvider.ComputeHash(stream);
  375. return false;
  376. }
  377. /// <summary>
  378. /// Adds a location reservation
  379. /// </summary>
  380. /// <param name="x">x coordinate</param>
  381. /// <param name="y">y coordinate</param>
  382. /// <returns></returns>
  383. override public ReservationData GetReservationAtPoint(uint x, uint y)
  384. {
  385. try
  386. {
  387. lock (database)
  388. {
  389. Dictionary<string, string> param = new Dictionary<string, string>();
  390. param["?x"] = x.ToString();
  391. param["?y"] = y.ToString();
  392. IDbCommand result =
  393. database.Query(
  394. "SELECT * FROM reservations WHERE resXMin <= ?x AND resXMax >= ?x AND resYMin <= ?y AND resYMax >= ?y",
  395. param);
  396. IDataReader reader = result.ExecuteReader();
  397. ReservationData row = database.readReservationRow(reader);
  398. reader.Close();
  399. result.Dispose();
  400. return row;
  401. }
  402. }
  403. catch (Exception e)
  404. {
  405. database.Reconnect();
  406. m_log.Error(e.ToString());
  407. return null;
  408. }
  409. }
  410. }
  411. }