/* EQ2Emulator: Everquest II Server Emulator Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) This file is part of EQ2Emulator. EQ2Emulator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. EQ2Emulator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with EQ2Emulator. If not, see . */ #include "debug.h" #include using namespace std; #include //#include #include #include "dbcore.h" #include #include #include #include "types.h" #include "MiscFunctions.h" #include "Log.h" #ifdef WIN32 #define snprintf _snprintf #define strncasecmp _strnicmp #define strcasecmp _stricmp #include #else #include "unix.h" #include #endif #ifdef _EQDEBUG #define DEBUG_MYSQL_QUERIES 0 #else #define DEBUG_MYSQL_QUERIES 0 #endif DBcore::DBcore() { mysql_init(&mysql); pHost = 0; pPort = 0; pUser = 0; pPassword = 0; pDatabase = 0; pCompress = false; pSSL = false; pStatus = Closed; } DBcore::~DBcore() { pStatus = Closed; mysql_close(&mysql); #if MYSQL_VERSION_ID >= 50003 mysql_library_end(); #else mysql_server_end(); #endif safe_delete_array(pHost); safe_delete_array(pUser); safe_delete_array(pPassword); safe_delete_array(pDatabase); } bool DBcore::ReadDBINI(char* host, char* user, char* passwd, char* database, int32& port, bool& compress, bool* items) { char line[256], * key, * val; bool on_database_section = false; FILE* f; if ((f = fopen(DB_INI_FILE, "r")) == NULL) { LogWrite(DATABASE__ERROR, 0, "DBCore", "Unable to open '%s' for reading", DB_INI_FILE); return false; } //read each line while (fgets(line, sizeof(line), f) != NULL) { //remove any new line or carriage return while ((key = strstr(line, "\n")) != NULL) *key = '\0'; while ((key = strstr(line, "\r")) != NULL) *key = '\0'; //ignore blank lines and commented lines if (strlen(line) == 0 || line[0] == '#') continue; key = strtok(line, "="); if (key == NULL) continue; //don't do anything until we find the [Database] section if (!on_database_section && strncasecmp(key, "[Database]", 10) == 0) on_database_section = true; else { val = strtok(NULL, "="); if (val == NULL) { if (strcasecmp(key, "password") == 0) { strcpy(passwd, ""); items[2] = true; } continue; } if (strcasecmp(key, "host") == 0) { strcpy(host, val); items[0] = true; } else if (strcasecmp(key, "user") == 0) { strcpy(user, val); items[1] = true; } else if (strcasecmp(key, "password") == 0) { strcpy(passwd, val); items[2] = true; } else if (strcasecmp(key, "database") == 0) { strcpy(database, val); items[3] = true; } else if (strcasecmp(key, "port") == 0) { port = atoi(val); items[4] = true; } else if (strcasecmp(key, "compression") == 0) { if (strcasecmp(val, "on") == 0) { compress = true; items[5] = true; LogWrite(DATABASE__INFO, 0, "DBCore", "DB Compression on."); } } } } fclose(f); if (!on_database_section) { LogWrite(DATABASE__ERROR, 0, "DBCore", "[Database] section not found in '%s'", DB_INI_FILE); return false; } return true; } // Sends the MySQL server a keepalive void DBcore::ping() { if (!MDatabase.trylock()) { // well, if's it's locked, someone's using it. If someone's using it, it doesnt need a keepalive return; } mysql_ping(&mysql); int32* errnum = new int32; *errnum = mysql_errno(&mysql); switch (*errnum) { case CR_COMMANDS_OUT_OF_SYNC: case CR_SERVER_GONE_ERROR: case CR_UNKNOWN_ERROR: { LogWrite(DATABASE__ERROR, 0, "DBCore", "[Database] We lost connection to the database., errno: %i", errno); break; } } safe_delete(errnum); MDatabase.unlock(); } bool DBcore::RunQuery(const char* query, int32 querylen, char* errbuf, MYSQL_RES** result, int32* affected_rows, int32* last_insert_id, int32* errnum, bool retry) { if (errnum) *errnum = 0; if (errbuf) errbuf[0] = 0; bool ret = false; LockMutex lock(&MDatabase); if (pStatus != Connected) Open(); LogWrite(DATABASE__QUERY, 0, "DBCore", query); if (mysql_real_query(&mysql, query, querylen)) { if (mysql_errno(&mysql) == CR_SERVER_GONE_ERROR) pStatus = Error; if (mysql_errno(&mysql) == CR_SERVER_LOST || mysql_errno(&mysql) == CR_SERVER_GONE_ERROR) { if (retry) { LogWrite(DATABASE__ERROR, 0, "DBCore", "Lost connection, attempting to recover..."); ret = RunQuery(query, querylen, errbuf, result, affected_rows, last_insert_id, errnum, false); } else { pStatus = Error; if (errnum) *errnum = mysql_errno(&mysql); if (errbuf) snprintf(errbuf, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql)); LogWrite(DATABASE__ERROR, 0, "DBCore", "#%i: %s\nQuery:\n%s", mysql_errno(&mysql), mysql_error(&mysql), query); ret = false; } } else { if (errnum) *errnum = mysql_errno(&mysql); if (errbuf) snprintf(errbuf, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql)); LogWrite(DATABASE__ERROR, 0, "DBCore", "#%i: %s\nQuery:\n%s", mysql_errno(&mysql), mysql_error(&mysql), query); ret = false; } } else { if (result && mysql_field_count(&mysql)) { *result = mysql_store_result(&mysql); } else if (result) *result = 0; if (affected_rows) *affected_rows = mysql_affected_rows(&mysql); if (last_insert_id) *last_insert_id = mysql_insert_id(&mysql); if (result) { if (*result) { ret = true; } else { if (errnum) *errnum = UINT_MAX; if (errbuf){ if((!affected_rows || (affected_rows && *affected_rows == 0)) && (!last_insert_id || (last_insert_id && *last_insert_id == 0))) LogWrite(DATABASE__RESULT, 1, "DBCore", "No Result."); } ret = false; } } else { ret = true; } } if (ret) { char tmp1[200] = {0}; char tmp2[200] = {0}; if (result && (*result)) snprintf(tmp1, sizeof(tmp1), ", %i rows returned", (int) mysql_num_rows(*result)); if (affected_rows) snprintf(tmp2, sizeof(tmp2), ", %i rows affected", (*affected_rows)); LogWrite(DATABASE__DEBUG, 0, "DBCore", "Query Successful%s%s", tmp1, tmp2); } else LogWrite(DATABASE__DEBUG, 0, "DBCore", "Query returned no results in %s!\n%s", __FUNCTION__, query); return ret; } int32 DBcore::DoEscapeString(char* tobuf, const char* frombuf, int32 fromlen) { LockMutex lock(&MDatabase); return mysql_real_escape_string(&mysql, tobuf, frombuf, fromlen); } 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) { LockMutex lock(&MDatabase); safe_delete_array(pHost); safe_delete_array(pUser); safe_delete_array(pPassword); safe_delete_array(pDatabase); pHost = new char[strlen(iHost) + 1]; strcpy(pHost, iHost); pUser = new char[strlen(iUser) + 1]; strcpy(pUser, iUser); pPassword = new char[strlen(iPassword) + 1]; strcpy(pPassword, iPassword); pDatabase = new char[strlen(iDatabase) + 1]; strcpy(pDatabase, iDatabase); pCompress = iCompress; pPort = iPort; pSSL = iSSL; return Open(errnum, errbuf); } bool DBcore::Open(int32* errnum, char* errbuf) { if (errbuf) errbuf[0] = 0; LockMutex lock(&MDatabase); if (GetStatus() == Connected) return true; if (GetStatus() == Error) mysql_close(&mysql); if (!pHost) return false; /* Quagmire - added CLIENT_FOUND_ROWS flag to the connect otherwise DB update calls would say 0 rows affected when the value already equalled what the function was tring to set it to, therefore the function would think it failed */ int32 flags = CLIENT_FOUND_ROWS; if (pCompress) flags |= CLIENT_COMPRESS; if (pSSL) flags |= CLIENT_SSL; if (mysql_real_connect(&mysql, pHost, pUser, pPassword, pDatabase, pPort, 0, flags)) { pStatus = Connected; return true; } else { if (errnum) *errnum = mysql_errno(&mysql); if (errbuf) snprintf(errbuf, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql)); pStatus = Error; return false; } } char* DBcore::getEscapeString(const char* from_string){ if(!from_string) from_string =""; int orig_size = strlen(from_string); int escape_size = (orig_size * 2) + 1; char* escaped = new char[escape_size]; memset(escaped, 0, escape_size); DoEscapeString(escaped, from_string, orig_size); return escaped; } string DBcore::getSafeEscapeString(const char* from_string){ if(!from_string) from_string =""; int orig_size = strlen(from_string); int escape_size = (orig_size * 2) + 1; char* escaped = new char[escape_size]; memset(escaped, 0, escape_size); DoEscapeString(escaped, from_string, orig_size); string ret = string(escaped); safe_delete_array(escaped); return ret; } string DBcore::getSafeEscapeString(string* from_string){ if(!from_string) return ""; int orig_size = from_string->length(); int escape_size = (orig_size * 2) + 1; char* escaped = new char[escape_size]; memset(escaped, 0, escape_size); DoEscapeString(escaped, from_string->c_str(), orig_size); string ret = string(escaped); safe_delete_array(escaped); return ret; }