MySQLUserData.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626
  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. */
  28. using System;
  29. using System.Collections.Generic;
  30. using System.Data;
  31. using System.Text.RegularExpressions;
  32. using libsecondlife;
  33. using OpenSim.Framework.Console;
  34. namespace OpenSim.Framework.Data.MySQL
  35. {
  36. /// <summary>
  37. /// A database interface class to a user profile storage system
  38. /// </summary>
  39. internal class MySQLUserData : IUserData
  40. {
  41. /// <summary>
  42. /// Database manager for MySQL
  43. /// </summary>
  44. public MySQLManager database;
  45. /// <summary>
  46. /// Loads and initialises the MySQL storage plugin
  47. /// </summary>
  48. public void Initialise()
  49. {
  50. // Load from an INI file connection details
  51. // TODO: move this to XML?
  52. IniFile GridDataMySqlFile = new IniFile("mysql_connection.ini");
  53. string settingHostname = GridDataMySqlFile.ParseFileReadValue("hostname");
  54. string settingDatabase = GridDataMySqlFile.ParseFileReadValue("database");
  55. string settingUsername = GridDataMySqlFile.ParseFileReadValue("username");
  56. string settingPassword = GridDataMySqlFile.ParseFileReadValue("password");
  57. string settingPooling = GridDataMySqlFile.ParseFileReadValue("pooling");
  58. string settingPort = GridDataMySqlFile.ParseFileReadValue("port");
  59. database =
  60. new MySQLManager(settingHostname, settingDatabase, settingUsername, settingPassword, settingPooling,
  61. settingPort);
  62. TestTables();
  63. }
  64. #region Test and initialization code
  65. /// <summary>
  66. /// Ensure that the user related tables exists and are at the latest version
  67. /// </summary>
  68. private void TestTables()
  69. {
  70. Dictionary<string, string> tableList = new Dictionary<string, string>();
  71. tableList["agents"] = null;
  72. tableList["users"] = null;
  73. tableList["userfriends"] = null;
  74. database.GetTableVersion(tableList);
  75. UpgradeAgentsTable(tableList["agents"]);
  76. UpgradeUsersTable(tableList["users"]);
  77. UpgradeFriendsTable(tableList["userfriends"]);
  78. }
  79. /// <summary>
  80. /// Create or upgrade the table if necessary
  81. /// </summary>
  82. /// <param name="oldVersion">A null indicates that the table does not
  83. /// currently exist</param>
  84. private void UpgradeAgentsTable(string oldVersion)
  85. {
  86. // null as the version, indicates that the table didn't exist
  87. if (oldVersion == null)
  88. {
  89. database.ExecuteResourceSql("CreateAgentsTable.sql");
  90. return;
  91. }
  92. }
  93. /// <summary>
  94. /// Create or upgrade the table if necessary
  95. /// </summary>
  96. /// <param name="oldVersion">A null indicates that the table does not
  97. /// currently exist</param>
  98. private void UpgradeUsersTable(string oldVersion)
  99. {
  100. // null as the version, indicates that the table didn't exist
  101. if (oldVersion == null)
  102. {
  103. database.ExecuteResourceSql("CreateUsersTable.sql");
  104. return;
  105. }
  106. else if (oldVersion.Contains("Rev. 1"))
  107. {
  108. database.ExecuteResourceSql("UpgradeUsersTableToVersion2.sql");
  109. return;
  110. }
  111. //MainLog.Instance.Verbose("DB","DBVers:" + oldVersion);
  112. }
  113. /// <summary>
  114. /// Create or upgrade the table if necessary
  115. /// </summary>
  116. /// <param name="oldVersion">A null indicates that the table does not
  117. /// currently exist</param>
  118. private void UpgradeFriendsTable(string oldVersion)
  119. {
  120. // null as the version, indicates that the table didn't exist
  121. if (oldVersion == null)
  122. {
  123. database.ExecuteResourceSql("CreateUserFriendsTable.sql");
  124. return;
  125. }
  126. }
  127. #endregion
  128. // see IUserData
  129. public UserProfileData GetUserByName(string user, string last)
  130. {
  131. try
  132. {
  133. lock (database)
  134. {
  135. Dictionary<string, string> param = new Dictionary<string, string>();
  136. param["?first"] = user;
  137. param["?second"] = last;
  138. IDbCommand result =
  139. database.Query("SELECT * FROM users WHERE username = ?first AND lastname = ?second", param);
  140. IDataReader reader = result.ExecuteReader();
  141. UserProfileData row = database.readUserRow(reader);
  142. reader.Close();
  143. result.Dispose();
  144. return row;
  145. }
  146. }
  147. catch (Exception e)
  148. {
  149. database.Reconnect();
  150. MainLog.Instance.Error(e.ToString());
  151. return null;
  152. }
  153. }
  154. #region User Friends List Data
  155. public void AddNewUserFriend(LLUUID friendlistowner, LLUUID friend, uint perms)
  156. {
  157. int dtvalue = Util.UnixTimeSinceEpoch();
  158. Dictionary<string, string> param = new Dictionary<string, string>();
  159. param["?ownerID"] = friendlistowner.UUID.ToString();
  160. param["?friendID"] = friend.UUID.ToString();
  161. param["?friendPerms"] = perms.ToString();
  162. param["?datetimestamp"] = dtvalue.ToString();
  163. try
  164. {
  165. lock (database)
  166. {
  167. IDbCommand adder =
  168. database.Query(
  169. "INSERT INTO `userfriends` " +
  170. "(`ownerID`,`friendID`,`friendPerms`,`datetimestamp`) " +
  171. "VALUES " +
  172. "(?ownerID,?friendID,?friendPerms,?datetimestamp)",
  173. param);
  174. adder.ExecuteNonQuery();
  175. adder =
  176. database.Query(
  177. "INSERT INTO `userfriends` " +
  178. "(`ownerID`,`friendID`,`friendPerms`,`datetimestamp`) " +
  179. "VALUES " +
  180. "(?friendID,?ownerID,?friendPerms,?datetimestamp)",
  181. param);
  182. adder.ExecuteNonQuery();
  183. }
  184. }
  185. catch (Exception e)
  186. {
  187. database.Reconnect();
  188. MainLog.Instance.Error(e.ToString());
  189. return;
  190. }
  191. }
  192. public void RemoveUserFriend(LLUUID friendlistowner, LLUUID friend)
  193. {
  194. Dictionary<string, string> param = new Dictionary<string, string>();
  195. param["?ownerID"] = friendlistowner.UUID.ToString();
  196. param["?friendID"] = friend.UUID.ToString();
  197. try
  198. {
  199. lock (database)
  200. {
  201. IDbCommand updater =
  202. database.Query(
  203. "delete from userfriends " +
  204. "where ownerID = ?ownerID and friendID = ?friendID",
  205. param);
  206. updater.ExecuteNonQuery();
  207. updater =
  208. database.Query(
  209. "delete from userfriends " +
  210. "where ownerID = ?friendID and friendID = ?ownerID",
  211. param);
  212. updater.ExecuteNonQuery();
  213. }
  214. }
  215. catch (Exception e)
  216. {
  217. database.Reconnect();
  218. MainLog.Instance.Error(e.ToString());
  219. return;
  220. }
  221. }
  222. public void UpdateUserFriendPerms(LLUUID friendlistowner, LLUUID friend, uint perms)
  223. {
  224. Dictionary<string, string> param = new Dictionary<string, string>();
  225. param["?ownerID"] = friendlistowner.UUID.ToString();
  226. param["?friendID"] = friend.UUID.ToString();
  227. param["?friendPerms"] = perms.ToString();
  228. try
  229. {
  230. lock (database)
  231. {
  232. IDbCommand updater =
  233. database.Query(
  234. "update userfriends " +
  235. "SET friendPerms = ?friendPerms " +
  236. "where ownerID = ?ownerID and friendID = ?friendID",
  237. param);
  238. updater.ExecuteNonQuery();
  239. }
  240. }
  241. catch (Exception e)
  242. {
  243. database.Reconnect();
  244. MainLog.Instance.Error(e.ToString());
  245. return;
  246. }
  247. }
  248. public List<FriendListItem> GetUserFriendList(LLUUID friendlistowner)
  249. {
  250. List<FriendListItem> Lfli = new List<FriendListItem>();
  251. Dictionary<string, string> param = new Dictionary<string, string>();
  252. param["?ownerID"] = friendlistowner.UUID.ToString();
  253. try
  254. {
  255. lock (database)
  256. {
  257. //Left Join userfriends to itself
  258. IDbCommand result =
  259. database.Query(
  260. "select a.ownerID,a.friendID,a.friendPerms,b.friendPerms as ownerperms from userfriends as a, userfriends as b" +
  261. " where a.ownerID = ?ownerID and b.ownerID = a.friendID and b.friendID = a.ownerID",
  262. param);
  263. IDataReader reader = result.ExecuteReader();
  264. while (reader.Read())
  265. {
  266. FriendListItem fli = new FriendListItem();
  267. fli.FriendListOwner = new LLUUID((string)reader["ownerID"]);
  268. fli.Friend = new LLUUID((string)reader["friendID"]);
  269. fli.FriendPerms = (uint)Convert.ToInt32(reader["friendPerms"]);
  270. // This is not a real column in the database table, it's a joined column from the opposite record
  271. fli.FriendListOwnerPerms = (uint)Convert.ToInt32(reader["ownerperms"]);
  272. Lfli.Add(fli);
  273. }
  274. reader.Close();
  275. result.Dispose();
  276. }
  277. }
  278. catch (Exception e)
  279. {
  280. database.Reconnect();
  281. MainLog.Instance.Error(e.ToString());
  282. return Lfli;
  283. }
  284. return Lfli;
  285. }
  286. #endregion
  287. public void UpdateUserCurrentRegion(LLUUID avatarid, LLUUID regionuuid)
  288. {
  289. MainLog.Instance.Verbose("USER", "Stub UpdateUserCUrrentRegion called");
  290. }
  291. public void LogOffUser(LLUUID avatarid)
  292. {
  293. MainLog.Instance.Verbose("USER", "Stub LogOffUser called");
  294. }
  295. public List<Framework.AvatarPickerAvatar> GeneratePickerResults(LLUUID queryID, string query)
  296. {
  297. List<Framework.AvatarPickerAvatar> returnlist = new List<Framework.AvatarPickerAvatar>();
  298. Regex objAlphaNumericPattern = new Regex("[^a-zA-Z0-9]");
  299. string[] querysplit;
  300. querysplit = query.Split(' ');
  301. if (querysplit.Length == 2)
  302. {
  303. Dictionary<string, string> param = new Dictionary<string, string>();
  304. param["?first"] = objAlphaNumericPattern.Replace(querysplit[0], "") + "%";
  305. param["?second"] = objAlphaNumericPattern.Replace(querysplit[1], "") + "%";
  306. try
  307. {
  308. lock (database)
  309. {
  310. IDbCommand result =
  311. database.Query(
  312. "SELECT UUID,username,lastname FROM users WHERE username like ?first AND lastname like ?second LIMIT 100",
  313. param);
  314. IDataReader reader = result.ExecuteReader();
  315. while (reader.Read())
  316. {
  317. Framework.AvatarPickerAvatar user = new Framework.AvatarPickerAvatar();
  318. user.AvatarID = new LLUUID((string) reader["UUID"]);
  319. user.firstName = (string) reader["username"];
  320. user.lastName = (string) reader["lastname"];
  321. returnlist.Add(user);
  322. }
  323. reader.Close();
  324. result.Dispose();
  325. }
  326. }
  327. catch (Exception e)
  328. {
  329. database.Reconnect();
  330. MainLog.Instance.Error(e.ToString());
  331. return returnlist;
  332. }
  333. }
  334. else if (querysplit.Length == 1)
  335. {
  336. try
  337. {
  338. lock (database)
  339. {
  340. Dictionary<string, string> param = new Dictionary<string, string>();
  341. param["?first"] = objAlphaNumericPattern.Replace(querysplit[0], "") + "%";
  342. IDbCommand result =
  343. database.Query(
  344. "SELECT UUID,username,lastname FROM users WHERE username like ?first OR lastname like ?first LIMIT 100",
  345. param);
  346. IDataReader reader = result.ExecuteReader();
  347. while (reader.Read())
  348. {
  349. Framework.AvatarPickerAvatar user = new Framework.AvatarPickerAvatar();
  350. user.AvatarID = new LLUUID((string) reader["UUID"]);
  351. user.firstName = (string) reader["username"];
  352. user.lastName = (string) reader["lastname"];
  353. returnlist.Add(user);
  354. }
  355. reader.Close();
  356. result.Dispose();
  357. }
  358. }
  359. catch (Exception e)
  360. {
  361. database.Reconnect();
  362. MainLog.Instance.Error(e.ToString());
  363. return returnlist;
  364. }
  365. }
  366. return returnlist;
  367. }
  368. // see IUserData
  369. public UserProfileData GetUserByUUID(LLUUID uuid)
  370. {
  371. try
  372. {
  373. lock (database)
  374. {
  375. Dictionary<string, string> param = new Dictionary<string, string>();
  376. param["?uuid"] = uuid.ToString();
  377. IDbCommand result = database.Query("SELECT * FROM users WHERE UUID = ?uuid", param);
  378. IDataReader reader = result.ExecuteReader();
  379. UserProfileData row = database.readUserRow(reader);
  380. reader.Close();
  381. result.Dispose();
  382. return row;
  383. }
  384. }
  385. catch (Exception e)
  386. {
  387. database.Reconnect();
  388. MainLog.Instance.Error(e.ToString());
  389. return null;
  390. }
  391. }
  392. /// <summary>
  393. /// Searches the database for a specified user profile by account
  394. /// </summary>
  395. /// <param name="uuid">The account</param>
  396. /// <returns>The users profile</returns>
  397. public UserProfileData GetUserByAccount(string account)
  398. {
  399. try
  400. {
  401. lock (database)
  402. {
  403. Dictionary<string, string> param = new Dictionary<string, string>();
  404. param["?account"] = account;
  405. IDbCommand result = database.Query("SELECT * FROM users WHERE account = ?account", param);
  406. IDataReader reader = result.ExecuteReader();
  407. UserProfileData row = database.readUserRow(reader);
  408. reader.Close();
  409. result.Dispose();
  410. return row;
  411. }
  412. }
  413. catch (Exception e)
  414. {
  415. database.Reconnect();
  416. MainLog.Instance.Error(e.ToString());
  417. return null;
  418. }
  419. }
  420. /// <summary>
  421. /// Returns a user session searching by name
  422. /// </summary>
  423. /// <param name="name">The account name</param>
  424. /// <returns>The users session</returns>
  425. public UserAgentData GetAgentByName(string name)
  426. {
  427. return GetAgentByName(name.Split(' ')[0], name.Split(' ')[1]);
  428. }
  429. /// <summary>
  430. /// Returns a user session by account name
  431. /// </summary>
  432. /// <param name="user">First part of the users account name</param>
  433. /// <param name="last">Second part of the users account name</param>
  434. /// <returns>The users session</returns>
  435. public UserAgentData GetAgentByName(string user, string last)
  436. {
  437. UserProfileData profile = GetUserByName(user, last);
  438. return GetAgentByUUID(profile.UUID);
  439. }
  440. /// <summary>
  441. /// Returns an agent session by account UUID
  442. /// </summary>
  443. /// <param name="uuid">The accounts UUID</param>
  444. /// <returns>The users session</returns>
  445. public UserAgentData GetAgentByUUID(LLUUID uuid)
  446. {
  447. try
  448. {
  449. lock (database)
  450. {
  451. Dictionary<string, string> param = new Dictionary<string, string>();
  452. param["?uuid"] = uuid.ToString();
  453. IDbCommand result = database.Query("SELECT * FROM agents WHERE UUID = ?uuid", param);
  454. IDataReader reader = result.ExecuteReader();
  455. UserAgentData row = database.readAgentRow(reader);
  456. reader.Close();
  457. result.Dispose();
  458. return row;
  459. }
  460. }
  461. catch (Exception e)
  462. {
  463. database.Reconnect();
  464. MainLog.Instance.Error(e.ToString());
  465. return null;
  466. }
  467. }
  468. /// <summary>
  469. /// Creates a new users profile
  470. /// </summary>
  471. /// <param name="user">The user profile to create</param>
  472. public void AddNewUserProfile(UserProfileData user)
  473. {
  474. try
  475. {
  476. lock (database)
  477. {
  478. database.insertUserRow(user.UUID, user.username, user.surname, user.passwordHash, user.passwordSalt,
  479. user.homeRegion, user.homeLocation.X, user.homeLocation.Y,
  480. user.homeLocation.Z,
  481. user.homeLookAt.X, user.homeLookAt.Y, user.homeLookAt.Z, user.created,
  482. user.lastLogin, user.userInventoryURI, user.userAssetURI,
  483. user.profileCanDoMask, user.profileWantDoMask,
  484. user.profileAboutText, user.profileFirstText, user.profileImage,
  485. user.profileFirstImage, user.webLoginKey);
  486. }
  487. }
  488. catch (Exception e)
  489. {
  490. database.Reconnect();
  491. MainLog.Instance.Error(e.ToString());
  492. }
  493. }
  494. /// <summary>
  495. /// Creates a new agent
  496. /// </summary>
  497. /// <param name="agent">The agent to create</param>
  498. public void AddNewUserAgent(UserAgentData agent)
  499. {
  500. // Do nothing.
  501. }
  502. /// <summary>
  503. /// Updates a user profile stored in the DB
  504. /// </summary>
  505. /// <param name="user">The profile data to use to update the DB</param>
  506. public bool UpdateUserProfile(UserProfileData user)
  507. {
  508. // TODO: implement
  509. return true;
  510. }
  511. /// <summary>
  512. /// Performs a money transfer request between two accounts
  513. /// </summary>
  514. /// <param name="from">The senders account ID</param>
  515. /// <param name="to">The receivers account ID</param>
  516. /// <param name="amount">The amount to transfer</param>
  517. /// <returns>Success?</returns>
  518. public bool MoneyTransferRequest(LLUUID from, LLUUID to, uint amount)
  519. {
  520. return false;
  521. }
  522. /// <summary>
  523. /// Performs an inventory transfer request between two accounts
  524. /// </summary>
  525. /// <remarks>TODO: Move to inventory server</remarks>
  526. /// <param name="from">The senders account ID</param>
  527. /// <param name="to">The receivers account ID</param>
  528. /// <param name="item">The item to transfer</param>
  529. /// <returns>Success?</returns>
  530. public bool InventoryTransferRequest(LLUUID from, LLUUID to, LLUUID item)
  531. {
  532. return false;
  533. }
  534. /// <summary>
  535. /// Database provider name
  536. /// </summary>
  537. /// <returns>Provider name</returns>
  538. public string getName()
  539. {
  540. return "MySQL Userdata Interface";
  541. }
  542. /// <summary>
  543. /// Database provider version
  544. /// </summary>
  545. /// <returns>provider version</returns>
  546. public string GetVersion()
  547. {
  548. return "0.1";
  549. }
  550. }
  551. }