dbcore.cpp 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  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. if (key == NULL)
  87. continue;
  88. //don't do anything until we find the [Database] section
  89. if (!on_database_section && strncasecmp(key, "[Database]", 10) == 0)
  90. on_database_section = true;
  91. else {
  92. val = strtok(NULL, "=");
  93. if (val == NULL)
  94. {
  95. if (strcasecmp(key, "password") == 0) {
  96. strcpy(passwd, "");
  97. items[2] = true;
  98. }
  99. continue;
  100. }
  101. if (strcasecmp(key, "host") == 0) {
  102. strcpy(host, val);
  103. items[0] = true;
  104. }
  105. else if (strcasecmp(key, "user") == 0) {
  106. strcpy(user, val);
  107. items[1] = true;
  108. }
  109. else if (strcasecmp(key, "password") == 0) {
  110. strcpy(passwd, val);
  111. items[2] = true;
  112. }
  113. else if (strcasecmp(key, "database") == 0) {
  114. strcpy(database, val);
  115. items[3] = true;
  116. }
  117. else if (strcasecmp(key, "port") == 0) {
  118. port = atoi(val);
  119. items[4] = true;
  120. }
  121. else if (strcasecmp(key, "compression") == 0) {
  122. if (strcasecmp(val, "on") == 0) {
  123. compress = true;
  124. items[5] = true;
  125. LogWrite(DATABASE__INFO, 0, "DBCore", "DB Compression on.");
  126. }
  127. }
  128. }
  129. }
  130. fclose(f);
  131. if (!on_database_section) {
  132. LogWrite(DATABASE__ERROR, 0, "DBCore", "[Database] section not found in '%s'", DB_INI_FILE);
  133. return false;
  134. }
  135. return true;
  136. }
  137. // Sends the MySQL server a keepalive
  138. void DBcore::ping() {
  139. if (!MDatabase.trylock()) {
  140. // well, if's it's locked, someone's using it. If someone's using it, it doesnt need a keepalive
  141. return;
  142. }
  143. mysql_ping(&mysql);
  144. int32* errnum = new int32;
  145. *errnum = mysql_errno(&mysql);
  146. switch (*errnum)
  147. {
  148. case CR_COMMANDS_OUT_OF_SYNC:
  149. case CR_SERVER_GONE_ERROR:
  150. case CR_UNKNOWN_ERROR:
  151. {
  152. LogWrite(DATABASE__ERROR, 0, "DBCore", "[Database] We lost connection to the database., errno: %i", errno);
  153. break;
  154. }
  155. }
  156. safe_delete(errnum);
  157. MDatabase.unlock();
  158. }
  159. bool DBcore::RunQuery(const char* query, int32 querylen, char* errbuf, MYSQL_RES** result, int32* affected_rows, int32* last_insert_id, int32* errnum, bool retry) {
  160. if (errnum)
  161. *errnum = 0;
  162. if (errbuf)
  163. errbuf[0] = 0;
  164. bool ret = false;
  165. LockMutex lock(&MDatabase);
  166. if (pStatus != Connected)
  167. Open();
  168. LogWrite(DATABASE__QUERY, 0, "DBCore", query);
  169. if (mysql_real_query(&mysql, query, querylen)) {
  170. if (mysql_errno(&mysql) == CR_SERVER_GONE_ERROR)
  171. pStatus = Error;
  172. if (mysql_errno(&mysql) == CR_SERVER_LOST || mysql_errno(&mysql) == CR_SERVER_GONE_ERROR) {
  173. if (retry) {
  174. LogWrite(DATABASE__ERROR, 0, "DBCore", "Lost connection, attempting to recover...");
  175. ret = RunQuery(query, querylen, errbuf, result, affected_rows, last_insert_id, errnum, false);
  176. }
  177. else {
  178. pStatus = Error;
  179. if (errnum)
  180. *errnum = mysql_errno(&mysql);
  181. if (errbuf)
  182. snprintf(errbuf, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql));
  183. LogWrite(DATABASE__ERROR, 0, "DBCore", "#%i: %s\nQuery:\n%s", mysql_errno(&mysql), mysql_error(&mysql), query);
  184. ret = false;
  185. }
  186. }
  187. else {
  188. if (errnum)
  189. *errnum = mysql_errno(&mysql);
  190. if (errbuf)
  191. snprintf(errbuf, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql));
  192. LogWrite(DATABASE__ERROR, 0, "DBCore", "#%i: %s\nQuery:\n%s", mysql_errno(&mysql), mysql_error(&mysql), query);
  193. ret = false;
  194. }
  195. }
  196. else {
  197. if (result && mysql_field_count(&mysql)) {
  198. *result = mysql_store_result(&mysql);
  199. }
  200. else if (result)
  201. *result = 0;
  202. if (affected_rows)
  203. *affected_rows = mysql_affected_rows(&mysql);
  204. if (last_insert_id)
  205. *last_insert_id = mysql_insert_id(&mysql);
  206. if (result) {
  207. if (*result) {
  208. ret = true;
  209. }
  210. else {
  211. if (errnum)
  212. *errnum = UINT_MAX;
  213. if (errbuf){
  214. if((!affected_rows || (affected_rows && *affected_rows == 0)) && (!last_insert_id || (last_insert_id && *last_insert_id == 0)))
  215. LogWrite(DATABASE__RESULT, 1, "DBCore", "No Result.");
  216. }
  217. ret = false;
  218. }
  219. }
  220. else {
  221. ret = true;
  222. }
  223. }
  224. if (ret)
  225. {
  226. char tmp1[200] = {0};
  227. char tmp2[200] = {0};
  228. if (result && (*result))
  229. snprintf(tmp1, sizeof(tmp1), ", %i rows returned", (int) mysql_num_rows(*result));
  230. if (affected_rows)
  231. snprintf(tmp2, sizeof(tmp2), ", %i rows affected", (*affected_rows));
  232. LogWrite(DATABASE__DEBUG, 0, "DBCore", "Query Successful%s%s", tmp1, tmp2);
  233. }
  234. else
  235. LogWrite(DATABASE__DEBUG, 0, "DBCore", "Query returned no results in %s!\n%s", __FUNCTION__, query);
  236. return ret;
  237. }
  238. int32 DBcore::DoEscapeString(char* tobuf, const char* frombuf, int32 fromlen) {
  239. LockMutex lock(&MDatabase);
  240. return mysql_real_escape_string(&mysql, tobuf, frombuf, fromlen);
  241. }
  242. 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) {
  243. LockMutex lock(&MDatabase);
  244. safe_delete_array(pHost);
  245. safe_delete_array(pUser);
  246. safe_delete_array(pPassword);
  247. safe_delete_array(pDatabase);
  248. pHost = new char[strlen(iHost) + 1];
  249. strcpy(pHost, iHost);
  250. pUser = new char[strlen(iUser) + 1];
  251. strcpy(pUser, iUser);
  252. pPassword = new char[strlen(iPassword) + 1];
  253. strcpy(pPassword, iPassword);
  254. pDatabase = new char[strlen(iDatabase) + 1];
  255. strcpy(pDatabase, iDatabase);
  256. pCompress = iCompress;
  257. pPort = iPort;
  258. pSSL = iSSL;
  259. return Open(errnum, errbuf);
  260. }
  261. bool DBcore::Open(int32* errnum, char* errbuf) {
  262. if (errbuf)
  263. errbuf[0] = 0;
  264. LockMutex lock(&MDatabase);
  265. if (GetStatus() == Connected)
  266. return true;
  267. if (GetStatus() == Error)
  268. mysql_close(&mysql);
  269. if (!pHost)
  270. return false;
  271. /*
  272. Quagmire - added CLIENT_FOUND_ROWS flag to the connect
  273. otherwise DB update calls would say 0 rows affected when the value already equalled
  274. what the function was tring to set it to, therefore the function would think it failed
  275. */
  276. int32 flags = CLIENT_FOUND_ROWS;
  277. if (pCompress)
  278. flags |= CLIENT_COMPRESS;
  279. if (pSSL)
  280. flags |= CLIENT_SSL;
  281. if (mysql_real_connect(&mysql, pHost, pUser, pPassword, pDatabase, pPort, 0, flags)) {
  282. pStatus = Connected;
  283. return true;
  284. }
  285. else {
  286. if (errnum)
  287. *errnum = mysql_errno(&mysql);
  288. if (errbuf)
  289. snprintf(errbuf, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql));
  290. pStatus = Error;
  291. return false;
  292. }
  293. }
  294. char* DBcore::getEscapeString(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. return escaped;
  303. }
  304. string DBcore::getSafeEscapeString(const char* from_string){
  305. if(!from_string)
  306. from_string ="";
  307. int orig_size = strlen(from_string);
  308. int escape_size = (orig_size * 2) + 1;
  309. char* escaped = new char[escape_size];
  310. memset(escaped, 0, escape_size);
  311. DoEscapeString(escaped, from_string, orig_size);
  312. string ret = string(escaped);
  313. safe_delete_array(escaped);
  314. return ret;
  315. }
  316. string DBcore::getSafeEscapeString(string* from_string){
  317. if(!from_string)
  318. return "";
  319. int orig_size = from_string->length();
  320. int escape_size = (orig_size * 2) + 1;
  321. char* escaped = new char[escape_size];
  322. memset(escaped, 0, escape_size);
  323. DoEscapeString(escaped, from_string->c_str(), orig_size);
  324. string ret = string(escaped);
  325. safe_delete_array(escaped);
  326. return ret;
  327. }