dbcore.cpp 9.5 KB

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