dbcore.cpp 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. /*
  2. EQ2Emulator: Everquest II Server Emulator
  3. Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
  4. This file is part of EQ2Emulator.
  5. EQ2Emulator is free software: you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation, either version 3 of the License, or
  8. (at your option) any later version.
  9. EQ2Emulator is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
  15. */
  16. #include "debug.h"
  17. #include <iostream>
  18. using namespace std;
  19. #include <errmsg.h>
  20. //#include <mysqld_error.h>
  21. #include <limits.h>
  22. #include "dbcore.h"
  23. #include <string.h>
  24. #include <stdio.h>
  25. #include <stdlib.h>
  26. #include "types.h"
  27. #include "MiscFunctions.h"
  28. #include "Log.h"
  29. #ifdef WIN32
  30. #define snprintf _snprintf
  31. #define strncasecmp _strnicmp
  32. #define strcasecmp _stricmp
  33. #include <process.h>
  34. #else
  35. #include "unix.h"
  36. #include <pthread.h>
  37. #endif
  38. #ifdef _EQDEBUG
  39. #define DEBUG_MYSQL_QUERIES 0
  40. #else
  41. #define DEBUG_MYSQL_QUERIES 0
  42. #endif
  43. DBcore::DBcore() {
  44. mysql_init(&mysql);
  45. pHost = 0;
  46. pPort = 0;
  47. pUser = 0;
  48. pPassword = 0;
  49. pDatabase = 0;
  50. pCompress = false;
  51. pSSL = false;
  52. pStatus = Closed;
  53. }
  54. DBcore::~DBcore() {
  55. pStatus = Closed;
  56. mysql_close(&mysql);
  57. #if MYSQL_VERSION_ID >= 50003
  58. mysql_library_end();
  59. #else
  60. mysql_server_end();
  61. #endif
  62. safe_delete_array(pHost);
  63. safe_delete_array(pUser);
  64. safe_delete_array(pPassword);
  65. safe_delete_array(pDatabase);
  66. }
  67. bool DBcore::ReadDBINI(char* host, char* user, char* passwd, char* database, int32& port, bool& compress, bool* items) {
  68. char line[256], * key, * val;
  69. bool on_database_section = false;
  70. FILE* f;
  71. if ((f = fopen(DB_INI_FILE, "r")) == NULL) {
  72. LogWrite(DATABASE__ERROR, 0, "DBCore", "Unable to open '%s' for reading", DB_INI_FILE);
  73. return false;
  74. }
  75. //read each line
  76. while (fgets(line, sizeof(line), f) != NULL) {
  77. //remove any new line or carriage return
  78. while ((key = strstr(line, "\n")) != NULL)
  79. *key = '\0';
  80. while ((key = strstr(line, "\r")) != NULL)
  81. *key = '\0';
  82. //ignore blank lines and commented lines
  83. if (strlen(line) == 0 || line[0] == '#')
  84. continue;
  85. key = strtok(line, "=");
  86. //don't do anything until we find the [Database] section
  87. if (!on_database_section && strncasecmp(key, "[Database]", 10) == 0)
  88. on_database_section = true;
  89. else {
  90. val = strtok(NULL, "=");
  91. if (strcasecmp(key, "host") == 0) {
  92. strcpy(host, val);
  93. items[0] = true;
  94. }
  95. else if (strcasecmp(key, "user") == 0) {
  96. strcpy(user, val);
  97. items[1] = true;
  98. }
  99. else if (strcasecmp(key, "password") == 0) {
  100. strcpy(passwd, val);
  101. items[2] = true;
  102. }
  103. else if (strcasecmp(key, "database") == 0) {
  104. strcpy(database, val);
  105. items[3] = true;
  106. }
  107. else if (strcasecmp(key, "port") == 0) {
  108. port = atoi(val);
  109. items[4] = true;
  110. }
  111. else if (strcasecmp(key, "compression") == 0) {
  112. if (strcasecmp(val, "on") == 0) {
  113. compress = true;
  114. items[5] = true;
  115. LogWrite(DATABASE__INFO, 0, "DBCore", "DB Compression on.");
  116. }
  117. }
  118. }
  119. }
  120. fclose(f);
  121. if (!on_database_section) {
  122. LogWrite(DATABASE__ERROR, 0, "DBCore", "[Database] section not found in '%s'", DB_INI_FILE);
  123. return false;
  124. }
  125. return true;
  126. }
  127. // Sends the MySQL server a keepalive
  128. void DBcore::ping() {
  129. if (!MDatabase.trylock()) {
  130. // well, if's it's locked, someone's using it. If someone's using it, it doesnt need a keepalive
  131. return;
  132. }
  133. mysql_ping(&mysql);
  134. int32* errnum = new int32;
  135. *errnum = mysql_errno(&mysql);
  136. switch (*errnum)
  137. {
  138. case CR_COMMANDS_OUT_OF_SYNC:
  139. case CR_SERVER_GONE_ERROR:
  140. case CR_UNKNOWN_ERROR:
  141. {
  142. LogWrite(DATABASE__ERROR, 0, "DBCore", "[Database] We lost connection to the database., errno: %i", errno);
  143. break;
  144. }
  145. }
  146. safe_delete(errnum);
  147. MDatabase.unlock();
  148. }
  149. bool DBcore::RunQuery(const char* query, int32 querylen, char* errbuf, MYSQL_RES** result, int32* affected_rows, int32* last_insert_id, int32* errnum, bool retry) {
  150. if (errnum)
  151. *errnum = 0;
  152. if (errbuf)
  153. errbuf[0] = 0;
  154. bool ret = false;
  155. LockMutex lock(&MDatabase);
  156. if (pStatus != Connected)
  157. Open();
  158. LogWrite(DATABASE__QUERY, 0, "DBCore", query);
  159. if (mysql_real_query(&mysql, query, querylen)) {
  160. if (mysql_errno(&mysql) == CR_SERVER_GONE_ERROR)
  161. pStatus = Error;
  162. if (mysql_errno(&mysql) == CR_SERVER_LOST || mysql_errno(&mysql) == CR_SERVER_GONE_ERROR) {
  163. if (retry) {
  164. LogWrite(DATABASE__ERROR, 0, "DBCore", "Lost connection, attempting to recover...");
  165. ret = RunQuery(query, querylen, errbuf, result, affected_rows, last_insert_id, errnum, false);
  166. }
  167. else {
  168. pStatus = Error;
  169. if (errnum)
  170. *errnum = mysql_errno(&mysql);
  171. if (errbuf)
  172. snprintf(errbuf, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql));
  173. LogWrite(DATABASE__ERROR, 0, "DBCore", "#%i: %s\nQuery:\n%s", mysql_errno(&mysql), mysql_error(&mysql), query);
  174. ret = false;
  175. }
  176. }
  177. else {
  178. if (errnum)
  179. *errnum = mysql_errno(&mysql);
  180. if (errbuf)
  181. snprintf(errbuf, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql));
  182. LogWrite(DATABASE__ERROR, 0, "DBCore", "#%i: %s\nQuery:\n%s", mysql_errno(&mysql), mysql_error(&mysql), query);
  183. ret = false;
  184. }
  185. }
  186. else {
  187. if (result && mysql_field_count(&mysql)) {
  188. *result = mysql_store_result(&mysql);
  189. }
  190. else if (result)
  191. *result = 0;
  192. if (affected_rows)
  193. *affected_rows = mysql_affected_rows(&mysql);
  194. if (last_insert_id)
  195. *last_insert_id = mysql_insert_id(&mysql);
  196. if (result) {
  197. if (*result) {
  198. ret = true;
  199. }
  200. else {
  201. if (errnum)
  202. *errnum = UINT_MAX;
  203. if (errbuf){
  204. if((!affected_rows || (affected_rows && *affected_rows == 0)) && (!last_insert_id || (last_insert_id && *last_insert_id == 0)))
  205. LogWrite(DATABASE__RESULT, 1, "DBCore", "No Result.");
  206. }
  207. ret = false;
  208. }
  209. }
  210. else {
  211. ret = true;
  212. }
  213. }
  214. if (ret)
  215. {
  216. char tmp1[200] = {0};
  217. char tmp2[200] = {0};
  218. if (result && (*result))
  219. snprintf(tmp1, sizeof(tmp1), ", %i rows returned", (int) mysql_num_rows(*result));
  220. if (affected_rows)
  221. snprintf(tmp2, sizeof(tmp2), ", %i rows affected", (*affected_rows));
  222. LogWrite(DATABASE__DEBUG, 0, "DBCore", "Query Successful%s%s", tmp1, tmp2);
  223. }
  224. else
  225. LogWrite(DATABASE__DEBUG, 0, "DBCore", "Query returned no results in %s!\n%s", __FUNCTION__, query);
  226. return ret;
  227. }
  228. int32 DBcore::DoEscapeString(char* tobuf, const char* frombuf, int32 fromlen) {
  229. LockMutex lock(&MDatabase);
  230. return mysql_real_escape_string(&mysql, tobuf, frombuf, fromlen);
  231. }
  232. bool DBcore::Open(const char* iHost, const char* iUser, const char* iPassword, const char* iDatabase,int32 iPort, int32* errnum, char* errbuf, bool iCompress, bool iSSL) {
  233. LockMutex lock(&MDatabase);
  234. safe_delete_array(pHost);
  235. safe_delete_array(pUser);
  236. safe_delete_array(pPassword);
  237. safe_delete_array(pDatabase);
  238. pHost = new char[strlen(iHost) + 1];
  239. strcpy(pHost, iHost);
  240. pUser = new char[strlen(iUser) + 1];
  241. strcpy(pUser, iUser);
  242. pPassword = new char[strlen(iPassword) + 1];
  243. strcpy(pPassword, iPassword);
  244. pDatabase = new char[strlen(iDatabase) + 1];
  245. strcpy(pDatabase, iDatabase);
  246. pCompress = iCompress;
  247. pPort = iPort;
  248. pSSL = iSSL;
  249. return Open(errnum, errbuf);
  250. }
  251. bool DBcore::Open(int32* errnum, char* errbuf) {
  252. if (errbuf)
  253. errbuf[0] = 0;
  254. LockMutex lock(&MDatabase);
  255. if (GetStatus() == Connected)
  256. return true;
  257. if (GetStatus() == Error)
  258. mysql_close(&mysql);
  259. if (!pHost)
  260. return false;
  261. /*
  262. Quagmire - added CLIENT_FOUND_ROWS flag to the connect
  263. otherwise DB update calls would say 0 rows affected when the value already equalled
  264. what the function was tring to set it to, therefore the function would think it failed
  265. */
  266. int32 flags = CLIENT_FOUND_ROWS;
  267. if (pCompress)
  268. flags |= CLIENT_COMPRESS;
  269. if (pSSL)
  270. flags |= CLIENT_SSL;
  271. if (mysql_real_connect(&mysql, pHost, pUser, pPassword, pDatabase, pPort, 0, flags)) {
  272. pStatus = Connected;
  273. return true;
  274. }
  275. else {
  276. if (errnum)
  277. *errnum = mysql_errno(&mysql);
  278. if (errbuf)
  279. snprintf(errbuf, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql));
  280. pStatus = Error;
  281. return false;
  282. }
  283. }
  284. char* DBcore::getEscapeString(const char* from_string){
  285. if(!from_string)
  286. from_string ="";
  287. int orig_size = strlen(from_string);
  288. int escape_size = (orig_size * 2) + 1;
  289. char* escaped = new char[escape_size];
  290. memset(escaped, 0, escape_size);
  291. DoEscapeString(escaped, from_string, orig_size);
  292. return escaped;
  293. }
  294. string DBcore::getSafeEscapeString(const char* from_string){
  295. if(!from_string)
  296. from_string ="";
  297. int orig_size = strlen(from_string);
  298. int escape_size = (orig_size * 2) + 1;
  299. char* escaped = new char[escape_size];
  300. memset(escaped, 0, escape_size);
  301. DoEscapeString(escaped, from_string, orig_size);
  302. string ret = string(escaped);
  303. safe_delete_array(escaped);
  304. return ret;
  305. }
  306. string DBcore::getSafeEscapeString(string* from_string){
  307. if(!from_string)
  308. return "";
  309. int orig_size = from_string->length();
  310. int escape_size = (orig_size * 2) + 1;
  311. char* escaped = new char[escape_size];
  312. memset(escaped, 0, escape_size);
  313. DoEscapeString(escaped, from_string->c_str(), orig_size);
  314. string ret = string(escaped);
  315. safe_delete_array(escaped);
  316. return ret;
  317. }