dbcore.cpp 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  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. MDatabase.unlock();
  135. }
  136. bool DBcore::RunQuery(const char* query, int32 querylen, char* errbuf, MYSQL_RES** result, int32* affected_rows, int32* last_insert_id, int32* errnum, bool retry) {
  137. if (errnum)
  138. *errnum = 0;
  139. if (errbuf)
  140. errbuf[0] = 0;
  141. bool ret = false;
  142. LockMutex lock(&MDatabase);
  143. if (pStatus != Connected)
  144. Open();
  145. LogWrite(DATABASE__QUERY, 0, "DBCore", query);
  146. if (mysql_real_query(&mysql, query, querylen)) {
  147. if (mysql_errno(&mysql) == CR_SERVER_GONE_ERROR)
  148. pStatus = Error;
  149. if (mysql_errno(&mysql) == CR_SERVER_LOST || mysql_errno(&mysql) == CR_SERVER_GONE_ERROR) {
  150. if (retry) {
  151. LogWrite(DATABASE__ERROR, 0, "DBCore", "Lost connection, attempting to recover...");
  152. ret = RunQuery(query, querylen, errbuf, result, affected_rows, last_insert_id, errnum, false);
  153. }
  154. else {
  155. pStatus = Error;
  156. if (errnum)
  157. *errnum = mysql_errno(&mysql);
  158. if (errbuf)
  159. snprintf(errbuf, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql));
  160. LogWrite(DATABASE__ERROR, 0, "DBCore", "#%i: %s\nQuery:\n%s", mysql_errno(&mysql), mysql_error(&mysql), query);
  161. ret = false;
  162. }
  163. }
  164. else {
  165. if (errnum)
  166. *errnum = mysql_errno(&mysql);
  167. if (errbuf)
  168. snprintf(errbuf, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql));
  169. LogWrite(DATABASE__ERROR, 0, "DBCore", "#%i: %s\nQuery:\n%s", mysql_errno(&mysql), mysql_error(&mysql), query);
  170. ret = false;
  171. }
  172. }
  173. else {
  174. if (result && mysql_field_count(&mysql)) {
  175. *result = mysql_store_result(&mysql);
  176. }
  177. else if (result)
  178. *result = 0;
  179. if (affected_rows)
  180. *affected_rows = mysql_affected_rows(&mysql);
  181. if (last_insert_id)
  182. *last_insert_id = mysql_insert_id(&mysql);
  183. if (result) {
  184. if (*result) {
  185. ret = true;
  186. }
  187. else {
  188. if (errnum)
  189. *errnum = UINT_MAX;
  190. if (errbuf){
  191. if((!affected_rows || (affected_rows && *affected_rows == 0)) && (!last_insert_id || (last_insert_id && *last_insert_id == 0)))
  192. LogWrite(DATABASE__RESULT, 1, "DBCore", "No Result.");
  193. }
  194. ret = false;
  195. }
  196. }
  197. else {
  198. ret = true;
  199. }
  200. }
  201. if (ret)
  202. {
  203. char tmp1[200] = {0};
  204. char tmp2[200] = {0};
  205. if (result && (*result))
  206. snprintf(tmp1, sizeof(tmp1), ", %i rows returned", (int) mysql_num_rows(*result));
  207. if (affected_rows)
  208. snprintf(tmp2, sizeof(tmp2), ", %i rows affected", (*affected_rows));
  209. LogWrite(DATABASE__DEBUG, 0, "DBCore", "Query Successful%s%s", tmp1, tmp2);
  210. }
  211. else
  212. LogWrite(DATABASE__DEBUG, 0, "DBCore", "Query returned no results in %s!\n%s", __FUNCTION__, query);
  213. return ret;
  214. }
  215. int32 DBcore::DoEscapeString(char* tobuf, const char* frombuf, int32 fromlen) {
  216. LockMutex lock(&MDatabase);
  217. return mysql_real_escape_string(&mysql, tobuf, frombuf, fromlen);
  218. }
  219. 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) {
  220. LockMutex lock(&MDatabase);
  221. safe_delete_array(pHost);
  222. safe_delete_array(pUser);
  223. safe_delete_array(pPassword);
  224. safe_delete_array(pDatabase);
  225. pHost = new char[strlen(iHost) + 1];
  226. strcpy(pHost, iHost);
  227. pUser = new char[strlen(iUser) + 1];
  228. strcpy(pUser, iUser);
  229. pPassword = new char[strlen(iPassword) + 1];
  230. strcpy(pPassword, iPassword);
  231. pDatabase = new char[strlen(iDatabase) + 1];
  232. strcpy(pDatabase, iDatabase);
  233. pCompress = iCompress;
  234. pPort = iPort;
  235. pSSL = iSSL;
  236. return Open(errnum, errbuf);
  237. }
  238. bool DBcore::Open(int32* errnum, char* errbuf) {
  239. if (errbuf)
  240. errbuf[0] = 0;
  241. LockMutex lock(&MDatabase);
  242. if (GetStatus() == Connected)
  243. return true;
  244. if (GetStatus() == Error)
  245. mysql_close(&mysql);
  246. if (!pHost)
  247. return false;
  248. /*
  249. Quagmire - added CLIENT_FOUND_ROWS flag to the connect
  250. otherwise DB update calls would say 0 rows affected when the value already equalled
  251. what the function was tring to set it to, therefore the function would think it failed
  252. */
  253. int32 flags = CLIENT_FOUND_ROWS;
  254. if (pCompress)
  255. flags |= CLIENT_COMPRESS;
  256. if (pSSL)
  257. flags |= CLIENT_SSL;
  258. if (mysql_real_connect(&mysql, pHost, pUser, pPassword, pDatabase, pPort, 0, flags)) {
  259. pStatus = Connected;
  260. return true;
  261. }
  262. else {
  263. if (errnum)
  264. *errnum = mysql_errno(&mysql);
  265. if (errbuf)
  266. snprintf(errbuf, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql));
  267. pStatus = Error;
  268. return false;
  269. }
  270. }
  271. char* DBcore::getEscapeString(const char* from_string){
  272. if(!from_string)
  273. from_string ="";
  274. int orig_size = strlen(from_string);
  275. int escape_size = (orig_size * 2) + 1;
  276. char* escaped = new char[escape_size];
  277. memset(escaped, 0, escape_size);
  278. DoEscapeString(escaped, from_string, orig_size);
  279. return escaped;
  280. }
  281. string DBcore::getSafeEscapeString(const char* from_string){
  282. if(!from_string)
  283. from_string ="";
  284. int orig_size = strlen(from_string);
  285. int escape_size = (orig_size * 2) + 1;
  286. char* escaped = new char[escape_size];
  287. memset(escaped, 0, escape_size);
  288. DoEscapeString(escaped, from_string, orig_size);
  289. string ret = string(escaped);
  290. safe_delete_array(escaped);
  291. return ret;
  292. }
  293. string DBcore::getSafeEscapeString(string* from_string){
  294. if(!from_string)
  295. return "";
  296. int orig_size = from_string->length();
  297. int escape_size = (orig_size * 2) + 1;
  298. char* escaped = new char[escape_size];
  299. memset(escaped, 0, escape_size);
  300. DoEscapeString(escaped, from_string->c_str(), orig_size);
  301. string ret = string(escaped);
  302. safe_delete_array(escaped);
  303. return ret;
  304. }