From 6813c20bb80ccbb390b320539b2d186aeb989f33 Mon Sep 17 00:00:00 2001 From: Andrei Karas Date: Tue, 16 Jan 2018 02:34:27 +0300 Subject: Remove suffix "_sql" from files in login directory. --- src/login/Makefile.in | 2 +- src/login/account.c | 871 +++++++++++++++++++++++++++++++++ src/login/account_sql.c | 871 --------------------------------- src/login/ipban.c | 308 ++++++++++++ src/login/ipban_sql.c | 308 ------------ src/login/loginlog.c | 225 +++++++++ src/login/loginlog_sql.c | 225 --------- vcproj-11/login-server.vcxproj | 6 +- vcproj-11/login-server.vcxproj.filters | 6 +- vcproj-12/login-server.vcxproj | 6 +- vcproj-12/login-server.vcxproj.filters | 6 +- vcproj-14/login-server.vcxproj | 6 +- vcproj-14/login-server.vcxproj.filters | 6 +- 13 files changed, 1423 insertions(+), 1423 deletions(-) create mode 100644 src/login/account.c delete mode 100644 src/login/account_sql.c create mode 100644 src/login/ipban.c delete mode 100644 src/login/ipban_sql.c create mode 100644 src/login/loginlog.c delete mode 100644 src/login/loginlog_sql.c diff --git a/src/login/Makefile.in b/src/login/Makefile.in index 69cc6a897..d0258deb0 100644 --- a/src/login/Makefile.in +++ b/src/login/Makefile.in @@ -40,7 +40,7 @@ MT19937AR_D = $(THIRDPARTY_D)/mt19937ar MT19937AR_OBJ = $(MT19937AR_D)/mt19937ar.o MT19937AR_H = $(MT19937AR_D)/mt19937ar.h -LOGIN_C = account_sql.c HPMlogin.c ipban_sql.c lclif.c login.c loginlog_sql.c +LOGIN_C = account.c HPMlogin.c ipban.c lclif.c login.c loginlog.c LOGIN_OBJ = $(addprefix obj_sql/, $(patsubst %.c,%.o,$(LOGIN_C))) LOGIN_H = login.h account.h HPMlogin.h ipban.h lclif.h loginlog.h LOGIN_PH = lclif.p.h diff --git a/src/login/account.c b/src/login/account.c new file mode 100644 index 000000000..66ede6cfa --- /dev/null +++ b/src/login/account.c @@ -0,0 +1,871 @@ +/** + * This file is part of Hercules. + * http://herc.ws - http://github.com/HerculesWS/Hercules + * + * Copyright (C) 2012-2016 Hercules Dev Team + * Copyright (C) Athena Dev Teams + * + * Hercules 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. + * + * This program 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 this program. If not, see . + */ +#define HERCULES_CORE + +#include "config/core.h" // CONSOLE_INPUT +#include "account.h" + +#include "common/cbasetypes.h" +#include "common/conf.h" +#include "common/console.h" +#include "common/memmgr.h" +#include "common/mmo.h" +#include "common/nullpo.h" +#include "common/showmsg.h" +#include "common/socket.h" +#include "common/sql.h" +#include "common/strlib.h" + +#include + +/// global defines +#define ACCOUNT_SQL_DB_VERSION 20110114 + +/// internal structure +typedef struct AccountDB_SQL +{ + AccountDB vtable; // public interface + + struct Sql *accounts; // SQL accounts storage + + // Sql settings + char db_hostname[32]; + uint16 db_port; + char db_username[32]; + char db_password[100]; + char db_database[32]; + char codepage[32]; + // other settings + bool case_sensitive; + char account_db[32]; + char global_acc_reg_num_db[32]; + char global_acc_reg_str_db[32]; + + +} AccountDB_SQL; + +/// internal structure +typedef struct AccountDBIterator_SQL +{ + AccountDBIterator vtable; // public interface + + AccountDB_SQL* db; + int last_account_id; +} AccountDBIterator_SQL; + +/// internal functions +static bool account_db_sql_init(AccountDB* self); +static void account_db_sql_destroy(AccountDB* self); +static bool account_db_sql_get_property(AccountDB* self, const char* key, char* buf, size_t buflen); +static bool account_db_sql_set_property(AccountDB* self, struct config_t *config, bool imported); +static bool account_db_sql_create(AccountDB* self, struct mmo_account* acc); +static bool account_db_sql_remove(AccountDB* self, const int account_id); +static bool account_db_sql_save(AccountDB* self, const struct mmo_account* acc); +static bool account_db_sql_load_num(AccountDB* self, struct mmo_account* acc, const int account_id); +static bool account_db_sql_load_str(AccountDB* self, struct mmo_account* acc, const char* userid); +static AccountDBIterator* account_db_sql_iterator(AccountDB* self); +static void account_db_sql_iter_destroy(AccountDBIterator* self); +static bool account_db_sql_iter_next(AccountDBIterator* self, struct mmo_account* acc); + +static bool mmo_auth_fromsql(AccountDB_SQL* db, struct mmo_account* acc, int account_id); +static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, bool is_new); + +/// public constructor +AccountDB* account_db_sql(void) +{ + AccountDB_SQL* db = (AccountDB_SQL*)aCalloc(1, sizeof(AccountDB_SQL)); + + // set up the vtable + db->vtable.init = &account_db_sql_init; + db->vtable.destroy = &account_db_sql_destroy; + db->vtable.get_property = &account_db_sql_get_property; + db->vtable.set_property = &account_db_sql_set_property; + db->vtable.save = &account_db_sql_save; + db->vtable.create = &account_db_sql_create; + db->vtable.remove = &account_db_sql_remove; + db->vtable.load_num = &account_db_sql_load_num; + db->vtable.load_str = &account_db_sql_load_str; + db->vtable.iterator = &account_db_sql_iterator; + + // initialize to default values + db->accounts = NULL; + // Sql settings + safestrncpy(db->db_hostname, "127.0.0.1", sizeof(db->db_hostname)); + db->db_port = 3306; + safestrncpy(db->db_username, "ragnarok", sizeof(db->db_username)); + safestrncpy(db->db_password, "ragnarok", sizeof(db->db_password)); + safestrncpy(db->db_database, "ragnarok", sizeof(db->db_database)); + safestrncpy(db->codepage, "", sizeof(db->codepage)); + // other settings + db->case_sensitive = false; + safestrncpy(db->account_db, "login", sizeof(db->account_db)); + safestrncpy(db->global_acc_reg_num_db, "global_acc_reg_num_db", sizeof(db->global_acc_reg_num_db)); + safestrncpy(db->global_acc_reg_str_db, "global_acc_reg_str_db", sizeof(db->global_acc_reg_str_db)); + + return &db->vtable; +} + + +/* ------------------------------------------------------------------------- */ + + +/// establishes database connection +static bool account_db_sql_init(AccountDB* self) +{ + AccountDB_SQL* db = (AccountDB_SQL*)self; + struct Sql *sql_handle = NULL; + + nullpo_ret(db); + + db->accounts = SQL->Malloc(); + sql_handle = db->accounts; + + if (SQL_ERROR == SQL->Connect(sql_handle, db->db_username, db->db_password, + db->db_hostname, db->db_port, db->db_database)) { + Sql_ShowDebug(sql_handle); + SQL->Free(db->accounts); + db->accounts = NULL; + return false; + } + + if (db->codepage[0] != '\0' && SQL_ERROR == SQL->SetEncoding(sql_handle, db->codepage)) + Sql_ShowDebug(sql_handle); + + Sql_HerculesUpdateCheck(db->accounts); +#ifdef CONSOLE_INPUT + console->input->setSQL(db->accounts); +#endif + return true; +} + +/// disconnects from database +static void account_db_sql_destroy(AccountDB* self) +{ + AccountDB_SQL* db = (AccountDB_SQL*)self; + + nullpo_retv(db); + SQL->Free(db->accounts); + db->accounts = NULL; + aFree(db); +} + +/// Gets a property from this database. +static bool account_db_sql_get_property(AccountDB* self, const char* key, char* buf, size_t buflen) +{ + /* TODO: + * This functionality is not being used as of now, it was removed in + * commit 5479f9631f8579d03fbfd14d8a49c7976226a156, it is meant to get + * engine properties when more than one engine is available. I'll + * re-add it as soon as I can, following the new standards. If anyone + * is interested in this functionality you can contact me in our boards + * and I'll try to add it sooner (Pan) [Panikon] + */ +#if 0 + AccountDB_SQL* db = (AccountDB_SQL*)self; + const char* signature; + + nullpo_ret(db); + nullpo_ret(key); + nullpo_ret(buf); + signature = "engine."; + if( strncmpi(key, signature, strlen(signature)) == 0 ) + { + key += strlen(signature); + if( strcmpi(key, "name") == 0 ) + safesnprintf(buf, buflen, "sql"); + else + if( strcmpi(key, "version") == 0 ) + safesnprintf(buf, buflen, "%d", ACCOUNT_SQL_DB_VERSION); + else + if( strcmpi(key, "comment") == 0 ) + safesnprintf(buf, buflen, "SQL Account Database"); + else + return false;// not found + return true; + } + + signature = "account.sql."; + if( strncmpi(key, signature, strlen(signature)) == 0 ) + { + key += strlen(signature); + if( strcmpi(key, "db_hostname") == 0 ) + safesnprintf(buf, buflen, "%s", db->db_hostname); + else + if( strcmpi(key, "db_port") == 0 ) + safesnprintf(buf, buflen, "%d", db->db_port); + else + if( strcmpi(key, "db_username") == 0 ) + safesnprintf(buf, buflen, "%s", db->db_username); + else + if( strcmpi(key, "db_password") == 0 ) + safesnprintf(buf, buflen, "%s", db->db_password); + else + if( strcmpi(key, "db_database") == 0 ) + safesnprintf(buf, buflen, "%s", db->db_database); + else + if( strcmpi(key, "codepage") == 0 ) + safesnprintf(buf, buflen, "%s", db->codepage); + else + if( strcmpi(key, "case_sensitive") == 0 ) + safesnprintf(buf, buflen, "%d", (db->case_sensitive ? 1 : 0)); + else + if( strcmpi(key, "account_db") == 0 ) + safesnprintf(buf, buflen, "%s", db->account_db); + else + if( strcmpi(key, "global_acc_reg_str_db") == 0 ) + safesnprintf(buf, buflen, "%s", db->global_acc_reg_str_db); + else + if( strcmpi(key, "global_acc_reg_num_db") == 0 ) + safesnprintf(buf, buflen, "%s", db->global_acc_reg_num_db); + else + return false;// not found + return true; + } + + return false;// not found +#endif // 0 + return false; +} + +/** + * Reads the 'inter_configuration' config file and initializes required variables. + * + * @param db Self. + * @param filename Path to configuration file + * @param imported Whether the current config is imported from another file. + * + * @retval false in case of error. + */ +bool account_db_read_inter(AccountDB_SQL *db, const char *filename, bool imported) +{ + struct config_t config; + struct config_setting_t *setting = NULL; + + nullpo_retr(false, db); + nullpo_retr(false, filename); + + if (!libconfig->load_file(&config, filename)) + return false; // Error message is already shown by libconfig->load_file + + if ((setting = libconfig->lookup(&config, "inter_configuration/database_names")) == NULL) { + libconfig->destroy(&config); + if (imported) + return true; + ShowError("account_db_sql_set_property: inter_configuration/database_names was not found!\n"); + return false; + } + libconfig->setting_lookup_mutable_string(setting, "account_db", db->account_db, sizeof(db->account_db)); + + if ((setting = libconfig->lookup(&config, "inter_configuration/database_names/registry")) == NULL) { + libconfig->destroy(&config); + if (imported) + return true; + ShowError("account_db_sql_set_property: inter_configuration/database_names/registry was not found!\n"); + return false; + } + libconfig->setting_lookup_mutable_string(setting, "global_acc_reg_str_db", db->global_acc_reg_str_db, sizeof(db->global_acc_reg_str_db)); + libconfig->setting_lookup_mutable_string(setting, "global_acc_reg_num_db", db->global_acc_reg_num_db, sizeof(db->global_acc_reg_num_db)); + + // TODO: Proper import mechanism for this file + + libconfig->destroy(&config); + return true; +} + +/** + * Loads the sql configuration. + * + * @param self Self. + * @param config The current config being parsed. + * @param imported Whether the current config is imported from another file. + * + * @retval false in case of error. + */ +static bool account_db_sql_set_property(AccountDB* self, struct config_t *config, bool imported) +{ + AccountDB_SQL* db = (AccountDB_SQL*)self; + struct config_setting_t *setting = NULL; + + nullpo_ret(db); + nullpo_ret(config); + + if ((setting = libconfig->lookup(config, "login_configuration/account/sql_connection")) == NULL) { + if (imported) + return true; + ShowError("account_db_sql_set_property: login_configuration/account/sql_connection was not found!\n"); + ShowWarning("account_db_sql_set_property: Defaulting sql_connection...\n"); + return false; + } + + libconfig->setting_lookup_mutable_string(setting, "db_hostname", db->db_hostname, sizeof(db->db_hostname)); + libconfig->setting_lookup_mutable_string(setting, "db_username", db->db_username, sizeof(db->db_username)); + libconfig->setting_lookup_mutable_string(setting, "db_password", db->db_password, sizeof(db->db_password)); + libconfig->setting_lookup_mutable_string(setting, "db_database", db->db_database, sizeof(db->db_database)); + libconfig->setting_lookup_mutable_string(setting, "codepage", db->codepage, sizeof(db->codepage)); // FIXME: Why do we need both codepage and default_codepage? + libconfig->setting_lookup_uint16(setting, "db_port", &db->db_port); + libconfig->setting_lookup_bool_real(setting, "case_sensitive", &db->case_sensitive); + + account_db_read_inter(db, "conf/common/inter-server.conf", imported); + + return true; +} + +/// create a new account entry +/// If acc->account_id is -1, the account id will be auto-generated, +/// and its value will be written to acc->account_id if everything succeeds. +static bool account_db_sql_create(AccountDB* self, struct mmo_account* acc) +{ + AccountDB_SQL* db = (AccountDB_SQL*)self; + struct Sql *sql_handle; + + // decide on the account id to assign + int account_id; + nullpo_ret(db); + nullpo_ret(acc); + sql_handle = db->accounts; + if( acc->account_id != -1 ) + {// caller specifies it manually + account_id = acc->account_id; + } + else + {// ask the database + char* data; + size_t len; + + if( SQL_SUCCESS != SQL->Query(sql_handle, "SELECT MAX(`account_id`)+1 FROM `%s`", db->account_db) ) + { + Sql_ShowDebug(sql_handle); + return false; + } + if( SQL_SUCCESS != SQL->NextRow(sql_handle) ) + { + Sql_ShowDebug(sql_handle); + SQL->FreeResult(sql_handle); + return false; + } + + SQL->GetData(sql_handle, 0, &data, &len); + account_id = ( data != NULL ) ? atoi(data) : 0; + SQL->FreeResult(sql_handle); + + if( account_id < START_ACCOUNT_NUM ) + account_id = START_ACCOUNT_NUM; + + } + + // zero value is prohibited + if( account_id == 0 ) + return false; + + // absolute maximum + if( account_id > END_ACCOUNT_NUM ) + return false; + + // insert the data into the database + acc->account_id = account_id; + return mmo_auth_tosql(db, acc, true); +} + +/// delete an existing account entry + its regs +static bool account_db_sql_remove(AccountDB* self, const int account_id) +{ + AccountDB_SQL* db = (AccountDB_SQL*)self; + struct Sql *sql_handle; + bool result = false; + + nullpo_ret(db); + sql_handle = db->accounts; + if( SQL_SUCCESS != SQL->QueryStr(sql_handle, "START TRANSACTION") + || SQL_SUCCESS != SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = %d", db->account_db, account_id) + || SQL_SUCCESS != SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = %d", db->global_acc_reg_num_db, account_id) + || SQL_SUCCESS != SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = %d", db->global_acc_reg_str_db, account_id) + ) + Sql_ShowDebug(sql_handle); + else + result = true; + + result &= ( SQL_SUCCESS == SQL->QueryStr(sql_handle, (result == true) ? "COMMIT" : "ROLLBACK") ); + + return result; +} + +/// update an existing account with the provided new data (both account and regs) +static bool account_db_sql_save(AccountDB* self, const struct mmo_account* acc) +{ + AccountDB_SQL* db = (AccountDB_SQL*)self; + return mmo_auth_tosql(db, acc, false); +} + +/// retrieve data from db and store it in the provided data structure +static bool account_db_sql_load_num(AccountDB* self, struct mmo_account* acc, const int account_id) +{ + AccountDB_SQL* db = (AccountDB_SQL*)self; + return mmo_auth_fromsql(db, acc, account_id); +} + +/// retrieve data from db and store it in the provided data structure +static bool account_db_sql_load_str(AccountDB* self, struct mmo_account* acc, const char* userid) +{ + AccountDB_SQL* db = (AccountDB_SQL*)self; + struct Sql *sql_handle; + char esc_userid[2*NAME_LENGTH+1]; + int account_id; + char* data; + + nullpo_ret(db); + sql_handle = db->accounts; + SQL->EscapeString(sql_handle, esc_userid, userid); + + // get the list of account IDs for this user ID + if( SQL_ERROR == SQL->Query(sql_handle, "SELECT `account_id` FROM `%s` WHERE `userid`= %s '%s'", + db->account_db, (db->case_sensitive ? "BINARY" : ""), esc_userid) ) + { + Sql_ShowDebug(sql_handle); + return false; + } + + if( SQL->NumRows(sql_handle) > 1 ) + {// serious problem - duplicate account + ShowError("account_db_sql_load_str: multiple accounts found when retrieving data for account '%s'!\n", userid); + SQL->FreeResult(sql_handle); + return false; + } + + if( SQL_SUCCESS != SQL->NextRow(sql_handle) ) + {// no such entry + SQL->FreeResult(sql_handle); + return false; + } + + SQL->GetData(sql_handle, 0, &data, NULL); + account_id = atoi(data); + + return account_db_sql_load_num(self, acc, account_id); +} + + +/// Returns a new forward iterator. +static AccountDBIterator* account_db_sql_iterator(AccountDB* self) +{ + AccountDB_SQL* db = (AccountDB_SQL*)self; + AccountDBIterator_SQL* iter; + + nullpo_retr(NULL, db); + iter = (AccountDBIterator_SQL*)aCalloc(1, sizeof(AccountDBIterator_SQL)); + // set up the vtable + iter->vtable.destroy = &account_db_sql_iter_destroy; + iter->vtable.next = &account_db_sql_iter_next; + + // fill data + iter->db = db; + iter->last_account_id = -1; + + return &iter->vtable; +} + + +/// Destroys this iterator, releasing all allocated memory (including itself). +static void account_db_sql_iter_destroy(AccountDBIterator* self) +{ + AccountDBIterator_SQL* iter = (AccountDBIterator_SQL*)self; + aFree(iter); +} + + +/// Fetches the next account in the database. +static bool account_db_sql_iter_next(AccountDBIterator* self, struct mmo_account* acc) +{ + AccountDBIterator_SQL* iter = (AccountDBIterator_SQL*)self; + AccountDB_SQL* db; + struct Sql *sql_handle; + char* data; + + nullpo_ret(iter); + db = (AccountDB_SQL*)iter->db; + nullpo_ret(db); + sql_handle = db->accounts; + // get next account ID + if( SQL_ERROR == SQL->Query(sql_handle, "SELECT `account_id` FROM `%s` WHERE `account_id` > '%d' ORDER BY `account_id` ASC LIMIT 1", + db->account_db, iter->last_account_id) ) + { + Sql_ShowDebug(sql_handle); + return false; + } + + if( SQL_SUCCESS == SQL->NextRow(sql_handle) && + SQL_SUCCESS == SQL->GetData(sql_handle, 0, &data, NULL) && + data != NULL ) + {// get account data + int account_id; + account_id = atoi(data); + if( mmo_auth_fromsql(db, acc, account_id) ) + { + iter->last_account_id = account_id; + SQL->FreeResult(sql_handle); + return true; + } + } + SQL->FreeResult(sql_handle); + return false; +} + + +static bool mmo_auth_fromsql(AccountDB_SQL* db, struct mmo_account* acc, int account_id) +{ + struct Sql *sql_handle; + char* data; + + nullpo_ret(db); + nullpo_ret(acc); + sql_handle = db->accounts; + // retrieve login entry for the specified account + if( SQL_ERROR == SQL->Query(sql_handle, + "SELECT `account_id`,`userid`,`user_pass`,`sex`,`email`,`group_id`,`state`,`unban_time`,`expiration_time`,`logincount`,`lastlogin`,`last_ip`,`birthdate`,`character_slots`,`pincode`,`pincode_change` FROM `%s` WHERE `account_id` = %d", + db->account_db, account_id ) + ) { + Sql_ShowDebug(sql_handle); + return false; + } + + if( SQL_SUCCESS != SQL->NextRow(sql_handle) ) + {// no such entry + SQL->FreeResult(sql_handle); + return false; + } + + SQL->GetData(sql_handle, 0, &data, NULL); acc->account_id = atoi(data); + SQL->GetData(sql_handle, 1, &data, NULL); safestrncpy(acc->userid, data, sizeof(acc->userid)); + SQL->GetData(sql_handle, 2, &data, NULL); safestrncpy(acc->pass, data, sizeof(acc->pass)); + SQL->GetData(sql_handle, 3, &data, NULL); acc->sex = data[0]; + SQL->GetData(sql_handle, 4, &data, NULL); safestrncpy(acc->email, data, sizeof(acc->email)); + SQL->GetData(sql_handle, 5, &data, NULL); acc->group_id = atoi(data); + SQL->GetData(sql_handle, 6, &data, NULL); acc->state = (unsigned int)strtoul(data, NULL, 10); + SQL->GetData(sql_handle, 7, &data, NULL); acc->unban_time = atol(data); + SQL->GetData(sql_handle, 8, &data, NULL); acc->expiration_time = atol(data); + SQL->GetData(sql_handle, 9, &data, NULL); acc->logincount = (unsigned int)strtoul(data, NULL, 10); + SQL->GetData(sql_handle, 10, &data, NULL); safestrncpy(acc->lastlogin, data != NULL ? data : "(never)", sizeof(acc->lastlogin)); + SQL->GetData(sql_handle, 11, &data, NULL); safestrncpy(acc->last_ip, data, sizeof(acc->last_ip)); + SQL->GetData(sql_handle, 12, &data, NULL); safestrncpy(acc->birthdate, data != NULL ? data : "0000-00-00", sizeof(acc->birthdate)); + SQL->GetData(sql_handle, 13, &data, NULL); acc->char_slots = (uint8)atoi(data); + SQL->GetData(sql_handle, 14, &data, NULL); safestrncpy(acc->pincode, data, sizeof(acc->pincode)); + SQL->GetData(sql_handle, 15, &data, NULL); acc->pincode_change = (unsigned int)atol(data); + + SQL->FreeResult(sql_handle); + + return true; +} + +static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, bool is_new) +{ + struct Sql *sql_handle; + struct SqlStmt *stmt; + bool result = false; + + nullpo_ret(db); + nullpo_ret(acc); + sql_handle = db->accounts; + stmt = SQL->StmtMalloc(sql_handle); + + // try + do + { + + if( SQL_SUCCESS != SQL->QueryStr(sql_handle, "START TRANSACTION") ) + { + Sql_ShowDebug(sql_handle); + break; + } + + if( is_new ) + {// insert into account table + if( SQL_SUCCESS != SQL->StmtPrepare(stmt, + "INSERT INTO `%s` (`account_id`, `userid`, `user_pass`, `sex`, `email`, `group_id`, `state`, `unban_time`, `expiration_time`, `logincount`, `lastlogin`, `last_ip`, `birthdate`, `character_slots`, `pincode`, `pincode_change`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + db->account_db) + || SQL_SUCCESS != SQL->StmtBindParam(stmt, 0, SQLDT_INT, &acc->account_id, sizeof acc->account_id) + || SQL_SUCCESS != SQL->StmtBindParam(stmt, 1, SQLDT_STRING, acc->userid, strlen(acc->userid)) + || SQL_SUCCESS != SQL->StmtBindParam(stmt, 2, SQLDT_STRING, acc->pass, strlen(acc->pass)) + || SQL_SUCCESS != SQL->StmtBindParam(stmt, 3, SQLDT_ENUM, &acc->sex, sizeof acc->sex) + || SQL_SUCCESS != SQL->StmtBindParam(stmt, 4, SQLDT_STRING, &acc->email, strlen(acc->email)) + || SQL_SUCCESS != SQL->StmtBindParam(stmt, 5, SQLDT_INT, &acc->group_id, sizeof acc->group_id) + || SQL_SUCCESS != SQL->StmtBindParam(stmt, 6, SQLDT_UINT, &acc->state, sizeof acc->state) + || SQL_SUCCESS != SQL->StmtBindParam(stmt, 7, SQLDT_TIME, &acc->unban_time, sizeof acc->unban_time) + || SQL_SUCCESS != SQL->StmtBindParam(stmt, 8, SQLDT_TIME, &acc->expiration_time, sizeof acc->expiration_time) + || SQL_SUCCESS != SQL->StmtBindParam(stmt, 9, SQLDT_UINT, &acc->logincount, sizeof acc->logincount) + || SQL_SUCCESS != (acc->lastlogin[0] < '1' || acc->lastlogin[0] > '9' ? + SQL->StmtBindParam(stmt, 10, SQLDT_NULL, NULL, 0) : + SQL->StmtBindParam(stmt, 10, SQLDT_STRING, &acc->lastlogin, strlen(acc->lastlogin)) + ) + || SQL_SUCCESS != SQL->StmtBindParam(stmt, 11, SQLDT_STRING, &acc->last_ip, strlen(acc->last_ip)) + || SQL_SUCCESS != (acc->birthdate[0] == '0' ? + SQL->StmtBindParam(stmt, 12, SQLDT_NULL, NULL, 0) : + SQL->StmtBindParam(stmt, 12, SQLDT_STRING, &acc->birthdate, strlen(acc->birthdate)) + ) + || SQL_SUCCESS != SQL->StmtBindParam(stmt, 13, SQLDT_UINT8, &acc->char_slots, sizeof acc->char_slots) + || SQL_SUCCESS != SQL->StmtBindParam(stmt, 14, SQLDT_STRING, &acc->pincode, strlen(acc->pincode)) + || SQL_SUCCESS != SQL->StmtBindParam(stmt, 15, SQLDT_UINT, &acc->pincode_change, sizeof acc->pincode_change) + || SQL_SUCCESS != SQL->StmtExecute(stmt) + ) { + SqlStmt_ShowDebug(stmt); + break; + } + } else {// update account table + if( SQL_SUCCESS != SQL->StmtPrepare(stmt, "UPDATE `%s` SET `userid`=?,`user_pass`=?,`sex`=?,`email`=?,`group_id`=?,`state`=?,`unban_time`=?,`expiration_time`=?,`logincount`=?,`lastlogin`=?,`last_ip`=?,`birthdate`=?,`character_slots`=?,`pincode`=?,`pincode_change`=? WHERE `account_id` = '%d'", db->account_db, acc->account_id) + || SQL_SUCCESS != SQL->StmtBindParam(stmt, 0, SQLDT_STRING, acc->userid, strlen(acc->userid)) + || SQL_SUCCESS != SQL->StmtBindParam(stmt, 1, SQLDT_STRING, acc->pass, strlen(acc->pass)) + || SQL_SUCCESS != SQL->StmtBindParam(stmt, 2, SQLDT_ENUM, &acc->sex, sizeof acc->sex) + || SQL_SUCCESS != SQL->StmtBindParam(stmt, 3, SQLDT_STRING, acc->email, strlen(acc->email)) + || SQL_SUCCESS != SQL->StmtBindParam(stmt, 4, SQLDT_INT, &acc->group_id, sizeof acc->group_id) + || SQL_SUCCESS != SQL->StmtBindParam(stmt, 5, SQLDT_UINT, &acc->state, sizeof acc->state) + || SQL_SUCCESS != SQL->StmtBindParam(stmt, 6, SQLDT_TIME, &acc->unban_time, sizeof acc->unban_time) + || SQL_SUCCESS != SQL->StmtBindParam(stmt, 7, SQLDT_TIME, &acc->expiration_time, sizeof acc->expiration_time) + || SQL_SUCCESS != SQL->StmtBindParam(stmt, 8, SQLDT_UINT, &acc->logincount, sizeof acc->logincount) + || SQL_SUCCESS != (acc->lastlogin[0] < '1' || acc->lastlogin[0] > '9' ? + SQL->StmtBindParam(stmt, 9, SQLDT_NULL, NULL, 0) : + SQL->StmtBindParam(stmt, 9, SQLDT_STRING, &acc->lastlogin, strlen(acc->lastlogin)) + ) + || SQL_SUCCESS != SQL->StmtBindParam(stmt, 10, SQLDT_STRING, &acc->last_ip, strlen(acc->last_ip)) + || SQL_SUCCESS != (acc->birthdate[0] == '0' ? + SQL->StmtBindParam(stmt, 11, SQLDT_NULL, NULL, 0) : + SQL->StmtBindParam(stmt, 11, SQLDT_STRING, &acc->birthdate, strlen(acc->birthdate)) + ) + || SQL_SUCCESS != SQL->StmtBindParam(stmt, 12, SQLDT_UINT8, &acc->char_slots, sizeof acc->char_slots) + || SQL_SUCCESS != SQL->StmtBindParam(stmt, 13, SQLDT_STRING, &acc->pincode, strlen(acc->pincode)) + || SQL_SUCCESS != SQL->StmtBindParam(stmt, 14, SQLDT_UINT, &acc->pincode_change, sizeof acc->pincode_change) + || SQL_SUCCESS != SQL->StmtExecute(stmt) + ) { + SqlStmt_ShowDebug(stmt); + break; + } + } + + // if we got this far, everything was successful + result = true; + + } while(0); + // finally + + result &= ( SQL_SUCCESS == SQL->QueryStr(sql_handle, (result == true) ? "COMMIT" : "ROLLBACK") ); + SQL->StmtFree(stmt); + + return result; +} + +struct Sql *account_db_sql_up(AccountDB* self) +{ + AccountDB_SQL* db = (AccountDB_SQL*)self; + return db ? db->accounts : NULL; +} +void mmo_save_accreg2(AccountDB* self, int fd, int account_id, int char_id) +{ + struct Sql *sql_handle; + AccountDB_SQL* db = (AccountDB_SQL*)self; + int count = RFIFOW(fd, 12); + + nullpo_retv(db); + sql_handle = db->accounts; + if (count) { + int cursor = 14, i; + char key[SCRIPT_VARNAME_LENGTH+1], sval[254]; + + for (i = 0; i < count; i++) { + unsigned int index; + int len = RFIFOB(fd, cursor); + safestrncpy(key, RFIFOP(fd, cursor + 1), min((int)sizeof(key), len)); + cursor += len + 1; + + index = RFIFOL(fd, cursor); + cursor += 4; + + switch (RFIFOB(fd, cursor++)) { + /* int */ + case 0: + if( SQL_ERROR == SQL->Query(sql_handle, "REPLACE INTO `%s` (`account_id`,`key`,`index`,`value`) VALUES ('%d','%s','%u','%u')", db->global_acc_reg_num_db, account_id, key, index, RFIFOL(fd, cursor)) ) + Sql_ShowDebug(sql_handle); + cursor += 4; + break; + case 1: + if( SQL_ERROR == SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = '%d' AND `key` = '%s' AND `index` = '%u' LIMIT 1", db->global_acc_reg_num_db, account_id, key, index) ) + Sql_ShowDebug(sql_handle); + break; + /* str */ + case 2: + len = RFIFOB(fd, cursor); + safestrncpy(sval, RFIFOP(fd, cursor + 1), min((int)sizeof(sval), len)); + cursor += len + 1; + if( SQL_ERROR == SQL->Query(sql_handle, "REPLACE INTO `%s` (`account_id`,`key`,`index`,`value`) VALUES ('%d','%s','%u','%s')", db->global_acc_reg_str_db, account_id, key, index, sval) ) + Sql_ShowDebug(sql_handle); + break; + case 3: + if( SQL_ERROR == SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = '%d' AND `key` = '%s' AND `index` = '%u' LIMIT 1", db->global_acc_reg_str_db, account_id, key, index) ) + Sql_ShowDebug(sql_handle); + break; + default: + ShowError("mmo_save_accreg2: DA HOO UNKNOWN TYPE %d\n",RFIFOB(fd, cursor - 1)); + return; + } + } + } +} + +void mmo_send_accreg2(AccountDB* self, int fd, int account_id, int char_id) +{ + struct Sql *sql_handle; + AccountDB_SQL* db = (AccountDB_SQL*)self; + char* data; + int plen = 0; + size_t len; + + nullpo_retv(db); + sql_handle = db->accounts; + if( SQL_ERROR == SQL->Query(sql_handle, "SELECT `key`, `index`, `value` FROM `%s` WHERE `account_id`='%d'", db->global_acc_reg_str_db, account_id) ) + Sql_ShowDebug(sql_handle); + + WFIFOHEAD(fd, 60000 + 300); + WFIFOW(fd, 0) = 0x3804; + /* 0x2 = length, set prior to being sent */ + WFIFOL(fd, 4) = account_id; + WFIFOL(fd, 8) = char_id; + WFIFOB(fd, 12) = 0;/* var type (only set when all vars have been sent, regardless of type) */ + WFIFOB(fd, 13) = 1;/* is string type */ + WFIFOW(fd, 14) = 0;/* count */ + plen = 16; + + /** + * Vessel! + * + * str type + * { keyLength(B), key(), index(L), valLength(B), val() } + **/ + while ( SQL_SUCCESS == SQL->NextRow(sql_handle) ) { + SQL->GetData(sql_handle, 0, &data, NULL); + len = strlen(data)+1; + + WFIFOB(fd, plen) = (unsigned char)len;/* won't be higher; the column size is 32 */ + plen += 1; + + safestrncpy(WFIFOP(fd,plen), data, len); + plen += len; + + SQL->GetData(sql_handle, 1, &data, NULL); + + WFIFOL(fd, plen) = (unsigned int)atol(data); + plen += 4; + + SQL->GetData(sql_handle, 2, &data, NULL); + len = strlen(data)+1; + + WFIFOB(fd, plen) = (unsigned char)len;/* won't be higher; the column size is 254 */ + plen += 1; + + safestrncpy(WFIFOP(fd,plen), data, len); + plen += len; + + WFIFOW(fd, 14) += 1; + + if( plen > 60000 ) { + WFIFOW(fd, 2) = plen; + WFIFOSET(fd, plen); + + /* prepare follow up */ + WFIFOHEAD(fd, 60000 + 300); + WFIFOW(fd, 0) = 0x3804; + /* 0x2 = length, set prior to being sent */ + WFIFOL(fd, 4) = account_id; + WFIFOL(fd, 8) = char_id; + WFIFOB(fd, 12) = 0;/* var type (only set when all vars have been sent, regardless of type) */ + WFIFOB(fd, 13) = 1;/* is string type */ + WFIFOW(fd, 14) = 0;/* count */ + plen = 16; + } + } + + /* mark & go. */ + WFIFOW(fd, 2) = plen; + WFIFOSET(fd, plen); + + SQL->FreeResult(sql_handle); + + if( SQL_ERROR == SQL->Query(sql_handle, "SELECT `key`, `index`, `value` FROM `%s` WHERE `account_id`='%d'", db->global_acc_reg_num_db, account_id) ) + Sql_ShowDebug(sql_handle); + + WFIFOHEAD(fd, 60000 + 300); + WFIFOW(fd, 0) = 0x3804; + /* 0x2 = length, set prior to being sent */ + WFIFOL(fd, 4) = account_id; + WFIFOL(fd, 8) = char_id; + WFIFOB(fd, 12) = 0;/* var type (only set when all vars have been sent, regardless of type) */ + WFIFOB(fd, 13) = 0;/* is int type */ + WFIFOW(fd, 14) = 0;/* count */ + plen = 16; + + /** + * Vessel! + * + * int type + * { keyLength(B), key(), index(L), value(L) } + **/ + while ( SQL_SUCCESS == SQL->NextRow(sql_handle) ) { + SQL->GetData(sql_handle, 0, &data, NULL); + len = strlen(data)+1; + + WFIFOB(fd, plen) = (unsigned char)len;/* won't be higher; the column size is 32 */ + plen += 1; + + safestrncpy(WFIFOP(fd,plen), data, len); + plen += len; + + SQL->GetData(sql_handle, 1, &data, NULL); + + WFIFOL(fd, plen) = (unsigned int)atol(data); + plen += 4; + + SQL->GetData(sql_handle, 2, &data, NULL); + + WFIFOL(fd, plen) = atoi(data); + plen += 4; + + WFIFOW(fd, 14) += 1; + + if( plen > 60000 ) { + WFIFOW(fd, 2) = plen; + WFIFOSET(fd, plen); + + /* prepare follow up */ + WFIFOHEAD(fd, 60000 + 300); + WFIFOW(fd, 0) = 0x3804; + /* 0x2 = length, set prior to being sent */ + WFIFOL(fd, 4) = account_id; + WFIFOL(fd, 8) = char_id; + WFIFOB(fd, 12) = 0;/* var type (only set when all vars have been sent, regardless of type) */ + WFIFOB(fd, 13) = 0;/* is int type */ + WFIFOW(fd, 14) = 0;/* count */ + + plen = 16; + } + } + + /* mark as complete & go. */ + WFIFOB(fd, 12) = 1; + WFIFOW(fd, 2) = plen; + WFIFOSET(fd, plen); + + SQL->FreeResult(sql_handle); +} diff --git a/src/login/account_sql.c b/src/login/account_sql.c deleted file mode 100644 index 66ede6cfa..000000000 --- a/src/login/account_sql.c +++ /dev/null @@ -1,871 +0,0 @@ -/** - * This file is part of Hercules. - * http://herc.ws - http://github.com/HerculesWS/Hercules - * - * Copyright (C) 2012-2016 Hercules Dev Team - * Copyright (C) Athena Dev Teams - * - * Hercules 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. - * - * This program 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 this program. If not, see . - */ -#define HERCULES_CORE - -#include "config/core.h" // CONSOLE_INPUT -#include "account.h" - -#include "common/cbasetypes.h" -#include "common/conf.h" -#include "common/console.h" -#include "common/memmgr.h" -#include "common/mmo.h" -#include "common/nullpo.h" -#include "common/showmsg.h" -#include "common/socket.h" -#include "common/sql.h" -#include "common/strlib.h" - -#include - -/// global defines -#define ACCOUNT_SQL_DB_VERSION 20110114 - -/// internal structure -typedef struct AccountDB_SQL -{ - AccountDB vtable; // public interface - - struct Sql *accounts; // SQL accounts storage - - // Sql settings - char db_hostname[32]; - uint16 db_port; - char db_username[32]; - char db_password[100]; - char db_database[32]; - char codepage[32]; - // other settings - bool case_sensitive; - char account_db[32]; - char global_acc_reg_num_db[32]; - char global_acc_reg_str_db[32]; - - -} AccountDB_SQL; - -/// internal structure -typedef struct AccountDBIterator_SQL -{ - AccountDBIterator vtable; // public interface - - AccountDB_SQL* db; - int last_account_id; -} AccountDBIterator_SQL; - -/// internal functions -static bool account_db_sql_init(AccountDB* self); -static void account_db_sql_destroy(AccountDB* self); -static bool account_db_sql_get_property(AccountDB* self, const char* key, char* buf, size_t buflen); -static bool account_db_sql_set_property(AccountDB* self, struct config_t *config, bool imported); -static bool account_db_sql_create(AccountDB* self, struct mmo_account* acc); -static bool account_db_sql_remove(AccountDB* self, const int account_id); -static bool account_db_sql_save(AccountDB* self, const struct mmo_account* acc); -static bool account_db_sql_load_num(AccountDB* self, struct mmo_account* acc, const int account_id); -static bool account_db_sql_load_str(AccountDB* self, struct mmo_account* acc, const char* userid); -static AccountDBIterator* account_db_sql_iterator(AccountDB* self); -static void account_db_sql_iter_destroy(AccountDBIterator* self); -static bool account_db_sql_iter_next(AccountDBIterator* self, struct mmo_account* acc); - -static bool mmo_auth_fromsql(AccountDB_SQL* db, struct mmo_account* acc, int account_id); -static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, bool is_new); - -/// public constructor -AccountDB* account_db_sql(void) -{ - AccountDB_SQL* db = (AccountDB_SQL*)aCalloc(1, sizeof(AccountDB_SQL)); - - // set up the vtable - db->vtable.init = &account_db_sql_init; - db->vtable.destroy = &account_db_sql_destroy; - db->vtable.get_property = &account_db_sql_get_property; - db->vtable.set_property = &account_db_sql_set_property; - db->vtable.save = &account_db_sql_save; - db->vtable.create = &account_db_sql_create; - db->vtable.remove = &account_db_sql_remove; - db->vtable.load_num = &account_db_sql_load_num; - db->vtable.load_str = &account_db_sql_load_str; - db->vtable.iterator = &account_db_sql_iterator; - - // initialize to default values - db->accounts = NULL; - // Sql settings - safestrncpy(db->db_hostname, "127.0.0.1", sizeof(db->db_hostname)); - db->db_port = 3306; - safestrncpy(db->db_username, "ragnarok", sizeof(db->db_username)); - safestrncpy(db->db_password, "ragnarok", sizeof(db->db_password)); - safestrncpy(db->db_database, "ragnarok", sizeof(db->db_database)); - safestrncpy(db->codepage, "", sizeof(db->codepage)); - // other settings - db->case_sensitive = false; - safestrncpy(db->account_db, "login", sizeof(db->account_db)); - safestrncpy(db->global_acc_reg_num_db, "global_acc_reg_num_db", sizeof(db->global_acc_reg_num_db)); - safestrncpy(db->global_acc_reg_str_db, "global_acc_reg_str_db", sizeof(db->global_acc_reg_str_db)); - - return &db->vtable; -} - - -/* ------------------------------------------------------------------------- */ - - -/// establishes database connection -static bool account_db_sql_init(AccountDB* self) -{ - AccountDB_SQL* db = (AccountDB_SQL*)self; - struct Sql *sql_handle = NULL; - - nullpo_ret(db); - - db->accounts = SQL->Malloc(); - sql_handle = db->accounts; - - if (SQL_ERROR == SQL->Connect(sql_handle, db->db_username, db->db_password, - db->db_hostname, db->db_port, db->db_database)) { - Sql_ShowDebug(sql_handle); - SQL->Free(db->accounts); - db->accounts = NULL; - return false; - } - - if (db->codepage[0] != '\0' && SQL_ERROR == SQL->SetEncoding(sql_handle, db->codepage)) - Sql_ShowDebug(sql_handle); - - Sql_HerculesUpdateCheck(db->accounts); -#ifdef CONSOLE_INPUT - console->input->setSQL(db->accounts); -#endif - return true; -} - -/// disconnects from database -static void account_db_sql_destroy(AccountDB* self) -{ - AccountDB_SQL* db = (AccountDB_SQL*)self; - - nullpo_retv(db); - SQL->Free(db->accounts); - db->accounts = NULL; - aFree(db); -} - -/// Gets a property from this database. -static bool account_db_sql_get_property(AccountDB* self, const char* key, char* buf, size_t buflen) -{ - /* TODO: - * This functionality is not being used as of now, it was removed in - * commit 5479f9631f8579d03fbfd14d8a49c7976226a156, it is meant to get - * engine properties when more than one engine is available. I'll - * re-add it as soon as I can, following the new standards. If anyone - * is interested in this functionality you can contact me in our boards - * and I'll try to add it sooner (Pan) [Panikon] - */ -#if 0 - AccountDB_SQL* db = (AccountDB_SQL*)self; - const char* signature; - - nullpo_ret(db); - nullpo_ret(key); - nullpo_ret(buf); - signature = "engine."; - if( strncmpi(key, signature, strlen(signature)) == 0 ) - { - key += strlen(signature); - if( strcmpi(key, "name") == 0 ) - safesnprintf(buf, buflen, "sql"); - else - if( strcmpi(key, "version") == 0 ) - safesnprintf(buf, buflen, "%d", ACCOUNT_SQL_DB_VERSION); - else - if( strcmpi(key, "comment") == 0 ) - safesnprintf(buf, buflen, "SQL Account Database"); - else - return false;// not found - return true; - } - - signature = "account.sql."; - if( strncmpi(key, signature, strlen(signature)) == 0 ) - { - key += strlen(signature); - if( strcmpi(key, "db_hostname") == 0 ) - safesnprintf(buf, buflen, "%s", db->db_hostname); - else - if( strcmpi(key, "db_port") == 0 ) - safesnprintf(buf, buflen, "%d", db->db_port); - else - if( strcmpi(key, "db_username") == 0 ) - safesnprintf(buf, buflen, "%s", db->db_username); - else - if( strcmpi(key, "db_password") == 0 ) - safesnprintf(buf, buflen, "%s", db->db_password); - else - if( strcmpi(key, "db_database") == 0 ) - safesnprintf(buf, buflen, "%s", db->db_database); - else - if( strcmpi(key, "codepage") == 0 ) - safesnprintf(buf, buflen, "%s", db->codepage); - else - if( strcmpi(key, "case_sensitive") == 0 ) - safesnprintf(buf, buflen, "%d", (db->case_sensitive ? 1 : 0)); - else - if( strcmpi(key, "account_db") == 0 ) - safesnprintf(buf, buflen, "%s", db->account_db); - else - if( strcmpi(key, "global_acc_reg_str_db") == 0 ) - safesnprintf(buf, buflen, "%s", db->global_acc_reg_str_db); - else - if( strcmpi(key, "global_acc_reg_num_db") == 0 ) - safesnprintf(buf, buflen, "%s", db->global_acc_reg_num_db); - else - return false;// not found - return true; - } - - return false;// not found -#endif // 0 - return false; -} - -/** - * Reads the 'inter_configuration' config file and initializes required variables. - * - * @param db Self. - * @param filename Path to configuration file - * @param imported Whether the current config is imported from another file. - * - * @retval false in case of error. - */ -bool account_db_read_inter(AccountDB_SQL *db, const char *filename, bool imported) -{ - struct config_t config; - struct config_setting_t *setting = NULL; - - nullpo_retr(false, db); - nullpo_retr(false, filename); - - if (!libconfig->load_file(&config, filename)) - return false; // Error message is already shown by libconfig->load_file - - if ((setting = libconfig->lookup(&config, "inter_configuration/database_names")) == NULL) { - libconfig->destroy(&config); - if (imported) - return true; - ShowError("account_db_sql_set_property: inter_configuration/database_names was not found!\n"); - return false; - } - libconfig->setting_lookup_mutable_string(setting, "account_db", db->account_db, sizeof(db->account_db)); - - if ((setting = libconfig->lookup(&config, "inter_configuration/database_names/registry")) == NULL) { - libconfig->destroy(&config); - if (imported) - return true; - ShowError("account_db_sql_set_property: inter_configuration/database_names/registry was not found!\n"); - return false; - } - libconfig->setting_lookup_mutable_string(setting, "global_acc_reg_str_db", db->global_acc_reg_str_db, sizeof(db->global_acc_reg_str_db)); - libconfig->setting_lookup_mutable_string(setting, "global_acc_reg_num_db", db->global_acc_reg_num_db, sizeof(db->global_acc_reg_num_db)); - - // TODO: Proper import mechanism for this file - - libconfig->destroy(&config); - return true; -} - -/** - * Loads the sql configuration. - * - * @param self Self. - * @param config The current config being parsed. - * @param imported Whether the current config is imported from another file. - * - * @retval false in case of error. - */ -static bool account_db_sql_set_property(AccountDB* self, struct config_t *config, bool imported) -{ - AccountDB_SQL* db = (AccountDB_SQL*)self; - struct config_setting_t *setting = NULL; - - nullpo_ret(db); - nullpo_ret(config); - - if ((setting = libconfig->lookup(config, "login_configuration/account/sql_connection")) == NULL) { - if (imported) - return true; - ShowError("account_db_sql_set_property: login_configuration/account/sql_connection was not found!\n"); - ShowWarning("account_db_sql_set_property: Defaulting sql_connection...\n"); - return false; - } - - libconfig->setting_lookup_mutable_string(setting, "db_hostname", db->db_hostname, sizeof(db->db_hostname)); - libconfig->setting_lookup_mutable_string(setting, "db_username", db->db_username, sizeof(db->db_username)); - libconfig->setting_lookup_mutable_string(setting, "db_password", db->db_password, sizeof(db->db_password)); - libconfig->setting_lookup_mutable_string(setting, "db_database", db->db_database, sizeof(db->db_database)); - libconfig->setting_lookup_mutable_string(setting, "codepage", db->codepage, sizeof(db->codepage)); // FIXME: Why do we need both codepage and default_codepage? - libconfig->setting_lookup_uint16(setting, "db_port", &db->db_port); - libconfig->setting_lookup_bool_real(setting, "case_sensitive", &db->case_sensitive); - - account_db_read_inter(db, "conf/common/inter-server.conf", imported); - - return true; -} - -/// create a new account entry -/// If acc->account_id is -1, the account id will be auto-generated, -/// and its value will be written to acc->account_id if everything succeeds. -static bool account_db_sql_create(AccountDB* self, struct mmo_account* acc) -{ - AccountDB_SQL* db = (AccountDB_SQL*)self; - struct Sql *sql_handle; - - // decide on the account id to assign - int account_id; - nullpo_ret(db); - nullpo_ret(acc); - sql_handle = db->accounts; - if( acc->account_id != -1 ) - {// caller specifies it manually - account_id = acc->account_id; - } - else - {// ask the database - char* data; - size_t len; - - if( SQL_SUCCESS != SQL->Query(sql_handle, "SELECT MAX(`account_id`)+1 FROM `%s`", db->account_db) ) - { - Sql_ShowDebug(sql_handle); - return false; - } - if( SQL_SUCCESS != SQL->NextRow(sql_handle) ) - { - Sql_ShowDebug(sql_handle); - SQL->FreeResult(sql_handle); - return false; - } - - SQL->GetData(sql_handle, 0, &data, &len); - account_id = ( data != NULL ) ? atoi(data) : 0; - SQL->FreeResult(sql_handle); - - if( account_id < START_ACCOUNT_NUM ) - account_id = START_ACCOUNT_NUM; - - } - - // zero value is prohibited - if( account_id == 0 ) - return false; - - // absolute maximum - if( account_id > END_ACCOUNT_NUM ) - return false; - - // insert the data into the database - acc->account_id = account_id; - return mmo_auth_tosql(db, acc, true); -} - -/// delete an existing account entry + its regs -static bool account_db_sql_remove(AccountDB* self, const int account_id) -{ - AccountDB_SQL* db = (AccountDB_SQL*)self; - struct Sql *sql_handle; - bool result = false; - - nullpo_ret(db); - sql_handle = db->accounts; - if( SQL_SUCCESS != SQL->QueryStr(sql_handle, "START TRANSACTION") - || SQL_SUCCESS != SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = %d", db->account_db, account_id) - || SQL_SUCCESS != SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = %d", db->global_acc_reg_num_db, account_id) - || SQL_SUCCESS != SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = %d", db->global_acc_reg_str_db, account_id) - ) - Sql_ShowDebug(sql_handle); - else - result = true; - - result &= ( SQL_SUCCESS == SQL->QueryStr(sql_handle, (result == true) ? "COMMIT" : "ROLLBACK") ); - - return result; -} - -/// update an existing account with the provided new data (both account and regs) -static bool account_db_sql_save(AccountDB* self, const struct mmo_account* acc) -{ - AccountDB_SQL* db = (AccountDB_SQL*)self; - return mmo_auth_tosql(db, acc, false); -} - -/// retrieve data from db and store it in the provided data structure -static bool account_db_sql_load_num(AccountDB* self, struct mmo_account* acc, const int account_id) -{ - AccountDB_SQL* db = (AccountDB_SQL*)self; - return mmo_auth_fromsql(db, acc, account_id); -} - -/// retrieve data from db and store it in the provided data structure -static bool account_db_sql_load_str(AccountDB* self, struct mmo_account* acc, const char* userid) -{ - AccountDB_SQL* db = (AccountDB_SQL*)self; - struct Sql *sql_handle; - char esc_userid[2*NAME_LENGTH+1]; - int account_id; - char* data; - - nullpo_ret(db); - sql_handle = db->accounts; - SQL->EscapeString(sql_handle, esc_userid, userid); - - // get the list of account IDs for this user ID - if( SQL_ERROR == SQL->Query(sql_handle, "SELECT `account_id` FROM `%s` WHERE `userid`= %s '%s'", - db->account_db, (db->case_sensitive ? "BINARY" : ""), esc_userid) ) - { - Sql_ShowDebug(sql_handle); - return false; - } - - if( SQL->NumRows(sql_handle) > 1 ) - {// serious problem - duplicate account - ShowError("account_db_sql_load_str: multiple accounts found when retrieving data for account '%s'!\n", userid); - SQL->FreeResult(sql_handle); - return false; - } - - if( SQL_SUCCESS != SQL->NextRow(sql_handle) ) - {// no such entry - SQL->FreeResult(sql_handle); - return false; - } - - SQL->GetData(sql_handle, 0, &data, NULL); - account_id = atoi(data); - - return account_db_sql_load_num(self, acc, account_id); -} - - -/// Returns a new forward iterator. -static AccountDBIterator* account_db_sql_iterator(AccountDB* self) -{ - AccountDB_SQL* db = (AccountDB_SQL*)self; - AccountDBIterator_SQL* iter; - - nullpo_retr(NULL, db); - iter = (AccountDBIterator_SQL*)aCalloc(1, sizeof(AccountDBIterator_SQL)); - // set up the vtable - iter->vtable.destroy = &account_db_sql_iter_destroy; - iter->vtable.next = &account_db_sql_iter_next; - - // fill data - iter->db = db; - iter->last_account_id = -1; - - return &iter->vtable; -} - - -/// Destroys this iterator, releasing all allocated memory (including itself). -static void account_db_sql_iter_destroy(AccountDBIterator* self) -{ - AccountDBIterator_SQL* iter = (AccountDBIterator_SQL*)self; - aFree(iter); -} - - -/// Fetches the next account in the database. -static bool account_db_sql_iter_next(AccountDBIterator* self, struct mmo_account* acc) -{ - AccountDBIterator_SQL* iter = (AccountDBIterator_SQL*)self; - AccountDB_SQL* db; - struct Sql *sql_handle; - char* data; - - nullpo_ret(iter); - db = (AccountDB_SQL*)iter->db; - nullpo_ret(db); - sql_handle = db->accounts; - // get next account ID - if( SQL_ERROR == SQL->Query(sql_handle, "SELECT `account_id` FROM `%s` WHERE `account_id` > '%d' ORDER BY `account_id` ASC LIMIT 1", - db->account_db, iter->last_account_id) ) - { - Sql_ShowDebug(sql_handle); - return false; - } - - if( SQL_SUCCESS == SQL->NextRow(sql_handle) && - SQL_SUCCESS == SQL->GetData(sql_handle, 0, &data, NULL) && - data != NULL ) - {// get account data - int account_id; - account_id = atoi(data); - if( mmo_auth_fromsql(db, acc, account_id) ) - { - iter->last_account_id = account_id; - SQL->FreeResult(sql_handle); - return true; - } - } - SQL->FreeResult(sql_handle); - return false; -} - - -static bool mmo_auth_fromsql(AccountDB_SQL* db, struct mmo_account* acc, int account_id) -{ - struct Sql *sql_handle; - char* data; - - nullpo_ret(db); - nullpo_ret(acc); - sql_handle = db->accounts; - // retrieve login entry for the specified account - if( SQL_ERROR == SQL->Query(sql_handle, - "SELECT `account_id`,`userid`,`user_pass`,`sex`,`email`,`group_id`,`state`,`unban_time`,`expiration_time`,`logincount`,`lastlogin`,`last_ip`,`birthdate`,`character_slots`,`pincode`,`pincode_change` FROM `%s` WHERE `account_id` = %d", - db->account_db, account_id ) - ) { - Sql_ShowDebug(sql_handle); - return false; - } - - if( SQL_SUCCESS != SQL->NextRow(sql_handle) ) - {// no such entry - SQL->FreeResult(sql_handle); - return false; - } - - SQL->GetData(sql_handle, 0, &data, NULL); acc->account_id = atoi(data); - SQL->GetData(sql_handle, 1, &data, NULL); safestrncpy(acc->userid, data, sizeof(acc->userid)); - SQL->GetData(sql_handle, 2, &data, NULL); safestrncpy(acc->pass, data, sizeof(acc->pass)); - SQL->GetData(sql_handle, 3, &data, NULL); acc->sex = data[0]; - SQL->GetData(sql_handle, 4, &data, NULL); safestrncpy(acc->email, data, sizeof(acc->email)); - SQL->GetData(sql_handle, 5, &data, NULL); acc->group_id = atoi(data); - SQL->GetData(sql_handle, 6, &data, NULL); acc->state = (unsigned int)strtoul(data, NULL, 10); - SQL->GetData(sql_handle, 7, &data, NULL); acc->unban_time = atol(data); - SQL->GetData(sql_handle, 8, &data, NULL); acc->expiration_time = atol(data); - SQL->GetData(sql_handle, 9, &data, NULL); acc->logincount = (unsigned int)strtoul(data, NULL, 10); - SQL->GetData(sql_handle, 10, &data, NULL); safestrncpy(acc->lastlogin, data != NULL ? data : "(never)", sizeof(acc->lastlogin)); - SQL->GetData(sql_handle, 11, &data, NULL); safestrncpy(acc->last_ip, data, sizeof(acc->last_ip)); - SQL->GetData(sql_handle, 12, &data, NULL); safestrncpy(acc->birthdate, data != NULL ? data : "0000-00-00", sizeof(acc->birthdate)); - SQL->GetData(sql_handle, 13, &data, NULL); acc->char_slots = (uint8)atoi(data); - SQL->GetData(sql_handle, 14, &data, NULL); safestrncpy(acc->pincode, data, sizeof(acc->pincode)); - SQL->GetData(sql_handle, 15, &data, NULL); acc->pincode_change = (unsigned int)atol(data); - - SQL->FreeResult(sql_handle); - - return true; -} - -static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, bool is_new) -{ - struct Sql *sql_handle; - struct SqlStmt *stmt; - bool result = false; - - nullpo_ret(db); - nullpo_ret(acc); - sql_handle = db->accounts; - stmt = SQL->StmtMalloc(sql_handle); - - // try - do - { - - if( SQL_SUCCESS != SQL->QueryStr(sql_handle, "START TRANSACTION") ) - { - Sql_ShowDebug(sql_handle); - break; - } - - if( is_new ) - {// insert into account table - if( SQL_SUCCESS != SQL->StmtPrepare(stmt, - "INSERT INTO `%s` (`account_id`, `userid`, `user_pass`, `sex`, `email`, `group_id`, `state`, `unban_time`, `expiration_time`, `logincount`, `lastlogin`, `last_ip`, `birthdate`, `character_slots`, `pincode`, `pincode_change`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", - db->account_db) - || SQL_SUCCESS != SQL->StmtBindParam(stmt, 0, SQLDT_INT, &acc->account_id, sizeof acc->account_id) - || SQL_SUCCESS != SQL->StmtBindParam(stmt, 1, SQLDT_STRING, acc->userid, strlen(acc->userid)) - || SQL_SUCCESS != SQL->StmtBindParam(stmt, 2, SQLDT_STRING, acc->pass, strlen(acc->pass)) - || SQL_SUCCESS != SQL->StmtBindParam(stmt, 3, SQLDT_ENUM, &acc->sex, sizeof acc->sex) - || SQL_SUCCESS != SQL->StmtBindParam(stmt, 4, SQLDT_STRING, &acc->email, strlen(acc->email)) - || SQL_SUCCESS != SQL->StmtBindParam(stmt, 5, SQLDT_INT, &acc->group_id, sizeof acc->group_id) - || SQL_SUCCESS != SQL->StmtBindParam(stmt, 6, SQLDT_UINT, &acc->state, sizeof acc->state) - || SQL_SUCCESS != SQL->StmtBindParam(stmt, 7, SQLDT_TIME, &acc->unban_time, sizeof acc->unban_time) - || SQL_SUCCESS != SQL->StmtBindParam(stmt, 8, SQLDT_TIME, &acc->expiration_time, sizeof acc->expiration_time) - || SQL_SUCCESS != SQL->StmtBindParam(stmt, 9, SQLDT_UINT, &acc->logincount, sizeof acc->logincount) - || SQL_SUCCESS != (acc->lastlogin[0] < '1' || acc->lastlogin[0] > '9' ? - SQL->StmtBindParam(stmt, 10, SQLDT_NULL, NULL, 0) : - SQL->StmtBindParam(stmt, 10, SQLDT_STRING, &acc->lastlogin, strlen(acc->lastlogin)) - ) - || SQL_SUCCESS != SQL->StmtBindParam(stmt, 11, SQLDT_STRING, &acc->last_ip, strlen(acc->last_ip)) - || SQL_SUCCESS != (acc->birthdate[0] == '0' ? - SQL->StmtBindParam(stmt, 12, SQLDT_NULL, NULL, 0) : - SQL->StmtBindParam(stmt, 12, SQLDT_STRING, &acc->birthdate, strlen(acc->birthdate)) - ) - || SQL_SUCCESS != SQL->StmtBindParam(stmt, 13, SQLDT_UINT8, &acc->char_slots, sizeof acc->char_slots) - || SQL_SUCCESS != SQL->StmtBindParam(stmt, 14, SQLDT_STRING, &acc->pincode, strlen(acc->pincode)) - || SQL_SUCCESS != SQL->StmtBindParam(stmt, 15, SQLDT_UINT, &acc->pincode_change, sizeof acc->pincode_change) - || SQL_SUCCESS != SQL->StmtExecute(stmt) - ) { - SqlStmt_ShowDebug(stmt); - break; - } - } else {// update account table - if( SQL_SUCCESS != SQL->StmtPrepare(stmt, "UPDATE `%s` SET `userid`=?,`user_pass`=?,`sex`=?,`email`=?,`group_id`=?,`state`=?,`unban_time`=?,`expiration_time`=?,`logincount`=?,`lastlogin`=?,`last_ip`=?,`birthdate`=?,`character_slots`=?,`pincode`=?,`pincode_change`=? WHERE `account_id` = '%d'", db->account_db, acc->account_id) - || SQL_SUCCESS != SQL->StmtBindParam(stmt, 0, SQLDT_STRING, acc->userid, strlen(acc->userid)) - || SQL_SUCCESS != SQL->StmtBindParam(stmt, 1, SQLDT_STRING, acc->pass, strlen(acc->pass)) - || SQL_SUCCESS != SQL->StmtBindParam(stmt, 2, SQLDT_ENUM, &acc->sex, sizeof acc->sex) - || SQL_SUCCESS != SQL->StmtBindParam(stmt, 3, SQLDT_STRING, acc->email, strlen(acc->email)) - || SQL_SUCCESS != SQL->StmtBindParam(stmt, 4, SQLDT_INT, &acc->group_id, sizeof acc->group_id) - || SQL_SUCCESS != SQL->StmtBindParam(stmt, 5, SQLDT_UINT, &acc->state, sizeof acc->state) - || SQL_SUCCESS != SQL->StmtBindParam(stmt, 6, SQLDT_TIME, &acc->unban_time, sizeof acc->unban_time) - || SQL_SUCCESS != SQL->StmtBindParam(stmt, 7, SQLDT_TIME, &acc->expiration_time, sizeof acc->expiration_time) - || SQL_SUCCESS != SQL->StmtBindParam(stmt, 8, SQLDT_UINT, &acc->logincount, sizeof acc->logincount) - || SQL_SUCCESS != (acc->lastlogin[0] < '1' || acc->lastlogin[0] > '9' ? - SQL->StmtBindParam(stmt, 9, SQLDT_NULL, NULL, 0) : - SQL->StmtBindParam(stmt, 9, SQLDT_STRING, &acc->lastlogin, strlen(acc->lastlogin)) - ) - || SQL_SUCCESS != SQL->StmtBindParam(stmt, 10, SQLDT_STRING, &acc->last_ip, strlen(acc->last_ip)) - || SQL_SUCCESS != (acc->birthdate[0] == '0' ? - SQL->StmtBindParam(stmt, 11, SQLDT_NULL, NULL, 0) : - SQL->StmtBindParam(stmt, 11, SQLDT_STRING, &acc->birthdate, strlen(acc->birthdate)) - ) - || SQL_SUCCESS != SQL->StmtBindParam(stmt, 12, SQLDT_UINT8, &acc->char_slots, sizeof acc->char_slots) - || SQL_SUCCESS != SQL->StmtBindParam(stmt, 13, SQLDT_STRING, &acc->pincode, strlen(acc->pincode)) - || SQL_SUCCESS != SQL->StmtBindParam(stmt, 14, SQLDT_UINT, &acc->pincode_change, sizeof acc->pincode_change) - || SQL_SUCCESS != SQL->StmtExecute(stmt) - ) { - SqlStmt_ShowDebug(stmt); - break; - } - } - - // if we got this far, everything was successful - result = true; - - } while(0); - // finally - - result &= ( SQL_SUCCESS == SQL->QueryStr(sql_handle, (result == true) ? "COMMIT" : "ROLLBACK") ); - SQL->StmtFree(stmt); - - return result; -} - -struct Sql *account_db_sql_up(AccountDB* self) -{ - AccountDB_SQL* db = (AccountDB_SQL*)self; - return db ? db->accounts : NULL; -} -void mmo_save_accreg2(AccountDB* self, int fd, int account_id, int char_id) -{ - struct Sql *sql_handle; - AccountDB_SQL* db = (AccountDB_SQL*)self; - int count = RFIFOW(fd, 12); - - nullpo_retv(db); - sql_handle = db->accounts; - if (count) { - int cursor = 14, i; - char key[SCRIPT_VARNAME_LENGTH+1], sval[254]; - - for (i = 0; i < count; i++) { - unsigned int index; - int len = RFIFOB(fd, cursor); - safestrncpy(key, RFIFOP(fd, cursor + 1), min((int)sizeof(key), len)); - cursor += len + 1; - - index = RFIFOL(fd, cursor); - cursor += 4; - - switch (RFIFOB(fd, cursor++)) { - /* int */ - case 0: - if( SQL_ERROR == SQL->Query(sql_handle, "REPLACE INTO `%s` (`account_id`,`key`,`index`,`value`) VALUES ('%d','%s','%u','%u')", db->global_acc_reg_num_db, account_id, key, index, RFIFOL(fd, cursor)) ) - Sql_ShowDebug(sql_handle); - cursor += 4; - break; - case 1: - if( SQL_ERROR == SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = '%d' AND `key` = '%s' AND `index` = '%u' LIMIT 1", db->global_acc_reg_num_db, account_id, key, index) ) - Sql_ShowDebug(sql_handle); - break; - /* str */ - case 2: - len = RFIFOB(fd, cursor); - safestrncpy(sval, RFIFOP(fd, cursor + 1), min((int)sizeof(sval), len)); - cursor += len + 1; - if( SQL_ERROR == SQL->Query(sql_handle, "REPLACE INTO `%s` (`account_id`,`key`,`index`,`value`) VALUES ('%d','%s','%u','%s')", db->global_acc_reg_str_db, account_id, key, index, sval) ) - Sql_ShowDebug(sql_handle); - break; - case 3: - if( SQL_ERROR == SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = '%d' AND `key` = '%s' AND `index` = '%u' LIMIT 1", db->global_acc_reg_str_db, account_id, key, index) ) - Sql_ShowDebug(sql_handle); - break; - default: - ShowError("mmo_save_accreg2: DA HOO UNKNOWN TYPE %d\n",RFIFOB(fd, cursor - 1)); - return; - } - } - } -} - -void mmo_send_accreg2(AccountDB* self, int fd, int account_id, int char_id) -{ - struct Sql *sql_handle; - AccountDB_SQL* db = (AccountDB_SQL*)self; - char* data; - int plen = 0; - size_t len; - - nullpo_retv(db); - sql_handle = db->accounts; - if( SQL_ERROR == SQL->Query(sql_handle, "SELECT `key`, `index`, `value` FROM `%s` WHERE `account_id`='%d'", db->global_acc_reg_str_db, account_id) ) - Sql_ShowDebug(sql_handle); - - WFIFOHEAD(fd, 60000 + 300); - WFIFOW(fd, 0) = 0x3804; - /* 0x2 = length, set prior to being sent */ - WFIFOL(fd, 4) = account_id; - WFIFOL(fd, 8) = char_id; - WFIFOB(fd, 12) = 0;/* var type (only set when all vars have been sent, regardless of type) */ - WFIFOB(fd, 13) = 1;/* is string type */ - WFIFOW(fd, 14) = 0;/* count */ - plen = 16; - - /** - * Vessel! - * - * str type - * { keyLength(B), key(), index(L), valLength(B), val() } - **/ - while ( SQL_SUCCESS == SQL->NextRow(sql_handle) ) { - SQL->GetData(sql_handle, 0, &data, NULL); - len = strlen(data)+1; - - WFIFOB(fd, plen) = (unsigned char)len;/* won't be higher; the column size is 32 */ - plen += 1; - - safestrncpy(WFIFOP(fd,plen), data, len); - plen += len; - - SQL->GetData(sql_handle, 1, &data, NULL); - - WFIFOL(fd, plen) = (unsigned int)atol(data); - plen += 4; - - SQL->GetData(sql_handle, 2, &data, NULL); - len = strlen(data)+1; - - WFIFOB(fd, plen) = (unsigned char)len;/* won't be higher; the column size is 254 */ - plen += 1; - - safestrncpy(WFIFOP(fd,plen), data, len); - plen += len; - - WFIFOW(fd, 14) += 1; - - if( plen > 60000 ) { - WFIFOW(fd, 2) = plen; - WFIFOSET(fd, plen); - - /* prepare follow up */ - WFIFOHEAD(fd, 60000 + 300); - WFIFOW(fd, 0) = 0x3804; - /* 0x2 = length, set prior to being sent */ - WFIFOL(fd, 4) = account_id; - WFIFOL(fd, 8) = char_id; - WFIFOB(fd, 12) = 0;/* var type (only set when all vars have been sent, regardless of type) */ - WFIFOB(fd, 13) = 1;/* is string type */ - WFIFOW(fd, 14) = 0;/* count */ - plen = 16; - } - } - - /* mark & go. */ - WFIFOW(fd, 2) = plen; - WFIFOSET(fd, plen); - - SQL->FreeResult(sql_handle); - - if( SQL_ERROR == SQL->Query(sql_handle, "SELECT `key`, `index`, `value` FROM `%s` WHERE `account_id`='%d'", db->global_acc_reg_num_db, account_id) ) - Sql_ShowDebug(sql_handle); - - WFIFOHEAD(fd, 60000 + 300); - WFIFOW(fd, 0) = 0x3804; - /* 0x2 = length, set prior to being sent */ - WFIFOL(fd, 4) = account_id; - WFIFOL(fd, 8) = char_id; - WFIFOB(fd, 12) = 0;/* var type (only set when all vars have been sent, regardless of type) */ - WFIFOB(fd, 13) = 0;/* is int type */ - WFIFOW(fd, 14) = 0;/* count */ - plen = 16; - - /** - * Vessel! - * - * int type - * { keyLength(B), key(), index(L), value(L) } - **/ - while ( SQL_SUCCESS == SQL->NextRow(sql_handle) ) { - SQL->GetData(sql_handle, 0, &data, NULL); - len = strlen(data)+1; - - WFIFOB(fd, plen) = (unsigned char)len;/* won't be higher; the column size is 32 */ - plen += 1; - - safestrncpy(WFIFOP(fd,plen), data, len); - plen += len; - - SQL->GetData(sql_handle, 1, &data, NULL); - - WFIFOL(fd, plen) = (unsigned int)atol(data); - plen += 4; - - SQL->GetData(sql_handle, 2, &data, NULL); - - WFIFOL(fd, plen) = atoi(data); - plen += 4; - - WFIFOW(fd, 14) += 1; - - if( plen > 60000 ) { - WFIFOW(fd, 2) = plen; - WFIFOSET(fd, plen); - - /* prepare follow up */ - WFIFOHEAD(fd, 60000 + 300); - WFIFOW(fd, 0) = 0x3804; - /* 0x2 = length, set prior to being sent */ - WFIFOL(fd, 4) = account_id; - WFIFOL(fd, 8) = char_id; - WFIFOB(fd, 12) = 0;/* var type (only set when all vars have been sent, regardless of type) */ - WFIFOB(fd, 13) = 0;/* is int type */ - WFIFOW(fd, 14) = 0;/* count */ - - plen = 16; - } - } - - /* mark as complete & go. */ - WFIFOB(fd, 12) = 1; - WFIFOW(fd, 2) = plen; - WFIFOSET(fd, plen); - - SQL->FreeResult(sql_handle); -} diff --git a/src/login/ipban.c b/src/login/ipban.c new file mode 100644 index 000000000..d74e6c4fa --- /dev/null +++ b/src/login/ipban.c @@ -0,0 +1,308 @@ +/** + * This file is part of Hercules. + * http://herc.ws - http://github.com/HerculesWS/Hercules + * + * Copyright (C) 2012-2016 Hercules Dev Team + * Copyright (C) Athena Dev Teams + * + * Hercules 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. + * + * This program 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 this program. If not, see . + */ +#define HERCULES_CORE + +#include "ipban.h" + +#include "login/login.h" +#include "login/loginlog.h" +#include "common/cbasetypes.h" +#include "common/conf.h" +#include "common/nullpo.h" +#include "common/showmsg.h" +#include "common/sql.h" +#include "common/strlib.h" +#include "common/timer.h" + +#include + +// Sql settings +static char ipban_db_hostname[32] = "127.0.0.1"; +static uint16 ipban_db_port = 3306; +static char ipban_db_username[32] = "ragnarok"; +static char ipban_db_password[100] = "ragnarok"; +static char ipban_db_database[32] = "ragnarok"; +static char ipban_codepage[32] = ""; +static char ipban_table[32] = "ipbanlist"; + +// globals +static struct Sql *sql_handle = NULL; +static int cleanup_timer_id = INVALID_TIMER; +static bool ipban_inited = false; + +int ipban_cleanup(int tid, int64 tick, int id, intptr_t data); + + +// initialize +void ipban_init(void) +{ + ipban_inited = true; + + if (!login->config->ipban) + return;// ipban disabled + + // establish connections + sql_handle = SQL->Malloc(); + if (SQL_ERROR == SQL->Connect(sql_handle, ipban_db_username, ipban_db_password, + ipban_db_hostname, ipban_db_port, ipban_db_database)) { + Sql_ShowDebug(sql_handle); + SQL->Free(sql_handle); + exit(EXIT_FAILURE); + } + if (ipban_codepage[0] != '\0' && SQL_ERROR == SQL->SetEncoding(sql_handle, ipban_codepage)) + Sql_ShowDebug(sql_handle); + + if (login->config->ipban_cleanup_interval > 0) { + // set up periodic cleanup of connection history and active bans + timer->add_func_list(ipban_cleanup, "ipban_cleanup"); + cleanup_timer_id = timer->add_interval(timer->gettick()+10, ipban_cleanup, 0, 0, login->config->ipban_cleanup_interval*1000); + } else { + // make sure it gets cleaned up on login-server start regardless of interval-based cleanups + ipban_cleanup(0,0,0,0); + } +} + +// finalize +void ipban_final(void) +{ + if (!login->config->ipban) + return;// ipban disabled + + if (login->config->ipban_cleanup_interval > 0) + // release data + timer->delete(cleanup_timer_id, ipban_cleanup); + + ipban_cleanup(0,0,0,0); // always clean up on login-server stop + + // close connections + SQL->Free(sql_handle); + sql_handle = NULL; +} + +/** + * Reads 'inter_configuration' and initializes required variables/Sets global + * configuration. + * + * @param filename Path to configuration file (used in error and warning messages). + * @param imported Whether the current config is imported from another file. + * + * @retval false in case of error. + + */ +bool ipban_config_read_inter(const char *filename, bool imported) +{ + struct config_t config; + struct config_setting_t *setting = NULL; + const char *import = NULL; + bool retval = true; + + nullpo_retr(false, filename); + + if (!libconfig->load_file(&config, filename)) + return false; // Error message is already shown by libconfig->read_file + + if ((setting = libconfig->lookup(&config, "inter_configuration/database_names")) == NULL) { + libconfig->destroy(&config); + if (imported) + return true; + ShowError("ipban_config_read: inter_configuration/database_names was not found!\n"); + return false; + } + libconfig->setting_lookup_mutable_string(setting, "ipban_table", ipban_table, sizeof(ipban_table)); + + // import should overwrite any previous configuration, so it should be called last + if (libconfig->lookup_string(&config, "import", &import) == CONFIG_TRUE) { + if (strcmp(import, filename) == 0 || strcmp(import, "conf/common/inter-server.conf") == 0) { + ShowWarning("ipban_config_read_inter: Loop detected! Skipping 'import'...\n"); + } else { + if (!ipban_config_read_inter(import, true)) + retval = false; + } + } + + libconfig->destroy(&config); + return retval; +} + +/** + * Reads login_configuration/account/ipban/sql_connection and loads configuration options. + * + * @param filename Path to configuration file (used in error and warning messages). + * @param config The current config being parsed. + * @param imported Whether the current config is imported from another file. + * + * @retval false in case of error. + */ +bool ipban_config_read_connection(const char *filename, struct config_t *config, bool imported) +{ + struct config_setting_t *setting = NULL; + + nullpo_retr(false, filename); + nullpo_retr(false, config); + + if ((setting = libconfig->lookup(config, "login_configuration/account/ipban/sql_connection")) == NULL) { + if (imported) + return true; + ShowError("account_db_sql_set_property: login_configuration/account/ipban/sql_connection was not found in %s!\n", filename); + return false; + } + + libconfig->setting_lookup_mutable_string(setting, "db_hostname", ipban_db_hostname, sizeof(ipban_db_hostname)); + libconfig->setting_lookup_mutable_string(setting, "db_database", ipban_db_database, sizeof(ipban_db_database)); + + libconfig->setting_lookup_mutable_string(setting, "db_username", ipban_db_username, sizeof(ipban_db_username)); + libconfig->setting_lookup_mutable_string(setting, "db_password", ipban_db_password, sizeof(ipban_db_password)); + libconfig->setting_lookup_mutable_string(setting, "codepage", ipban_codepage, sizeof(ipban_codepage)); + libconfig->setting_lookup_uint16(setting, "db_port", &ipban_db_port); + + return true; +} + +/** + * Reads login_configuration/account/ipban/dynamic_pass_failure and loads configuration options. + * + * @param filename Path to configuration file (used in error and warning messages). + * @param config The current config being parsed. + * @param imported Whether the current config is imported from another file. + * + * @retval false in case of error. + */ +bool ipban_config_read_dynamic(const char *filename, struct config_t *config, bool imported) +{ + struct config_setting_t *setting = NULL; + + nullpo_retr(false, filename); + nullpo_retr(false, config); + + if ((setting = libconfig->lookup(config, "login_configuration/account/ipban/dynamic_pass_failure")) == NULL) { + if (imported) + return true; + ShowError("account_db_sql_set_property: login_configuration/account/ipban/dynamic_pass_failure was not found in %s!\n", filename); + return false; + } + + libconfig->setting_lookup_bool_real(setting, "enabled", &login->config->dynamic_pass_failure_ban); + libconfig->setting_lookup_uint32(setting, "ban_interval", &login->config->dynamic_pass_failure_ban_interval); + libconfig->setting_lookup_uint32(setting, "ban_limit", &login->config->dynamic_pass_failure_ban_limit); + libconfig->setting_lookup_uint32(setting, "ban_duration", &login->config->dynamic_pass_failure_ban_duration); + + return true; +} + +/** + * Reads login_configuration.account.ipban and loads configuration options. + * + * @param filename Path to configuration file (used in error and warning messages). + * @param config The current config being parsed. + * @param imported Whether the current config is imported from another file. + * + * @retval false in case of error. + */ +bool ipban_config_read(const char *filename, struct config_t *config, bool imported) +{ + struct config_setting_t *setting = NULL; + bool retval = true; + + nullpo_retr(false, filename); + nullpo_retr(false, config); + + if (ipban_inited) + return false; // settings can only be changed before init + + if ((setting = libconfig->lookup(config, "login_configuration/account/ipban")) == NULL) { + if (!imported) + ShowError("login_config_read: login_configuration/log was not found in %s!\n", filename); + return false; + } + + libconfig->setting_lookup_bool_real(setting, "enabled", &login->config->ipban); + libconfig->setting_lookup_uint32(setting, "cleanup_interval", &login->config->ipban_cleanup_interval); + + if (!ipban_config_read_inter("conf/common/inter-server.conf", imported)) + retval = false; + if (!ipban_config_read_connection(filename, config, imported)) + retval = false; + if (!ipban_config_read_dynamic(filename, config, imported)) + retval = false; + + return retval; +} + +// check ip against active bans list +bool ipban_check(uint32 ip) +{ + uint8* p = (uint8*)&ip; + char* data = NULL; + int matches; + + if (!login->config->ipban) + return false;// ipban disabled + + if( SQL_ERROR == SQL->Query(sql_handle, "SELECT count(*) FROM `%s` WHERE `rtime` > NOW() AND (`list` = '%u.*.*.*' OR `list` = '%u.%u.*.*' OR `list` = '%u.%u.%u.*' OR `list` = '%u.%u.%u.%u')", + ipban_table, p[3], p[3], p[2], p[3], p[2], p[1], p[3], p[2], p[1], p[0]) ) + { + Sql_ShowDebug(sql_handle); + // close connection because we can't verify their connectivity. + return true; + } + + if( SQL_SUCCESS != SQL->NextRow(sql_handle) ) + return false; + + SQL->GetData(sql_handle, 0, &data, NULL); + matches = atoi(data); + SQL->FreeResult(sql_handle); + + return( matches > 0 ); +} + +// log failed attempt +void ipban_log(uint32 ip) +{ + unsigned long failures; + + if (!login->config->ipban) + return;// ipban disabled + + failures = loginlog_failedattempts(ip, login->config->dynamic_pass_failure_ban_interval);// how many times failed account? in one ip. + + // if over the limit, add a temporary ban entry + if (failures >= login->config->dynamic_pass_failure_ban_limit) + { + uint8* p = (uint8*)&ip; + if (SQL_ERROR == SQL->Query(sql_handle, "INSERT INTO `%s`(`list`,`btime`,`rtime`,`reason`) VALUES ('%u.%u.%u.*', NOW() , NOW() + INTERVAL %u MINUTE ,'Password error ban')", + ipban_table, p[3], p[2], p[1], login->config->dynamic_pass_failure_ban_duration)) + { + Sql_ShowDebug(sql_handle); + } + } +} + +// remove expired bans +int ipban_cleanup(int tid, int64 tick, int id, intptr_t data) { + if (!login->config->ipban) + return 0;// ipban disabled + + if( SQL_ERROR == SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `rtime` <= NOW()", ipban_table) ) + Sql_ShowDebug(sql_handle); + + return 0; +} diff --git a/src/login/ipban_sql.c b/src/login/ipban_sql.c deleted file mode 100644 index d74e6c4fa..000000000 --- a/src/login/ipban_sql.c +++ /dev/null @@ -1,308 +0,0 @@ -/** - * This file is part of Hercules. - * http://herc.ws - http://github.com/HerculesWS/Hercules - * - * Copyright (C) 2012-2016 Hercules Dev Team - * Copyright (C) Athena Dev Teams - * - * Hercules 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. - * - * This program 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 this program. If not, see . - */ -#define HERCULES_CORE - -#include "ipban.h" - -#include "login/login.h" -#include "login/loginlog.h" -#include "common/cbasetypes.h" -#include "common/conf.h" -#include "common/nullpo.h" -#include "common/showmsg.h" -#include "common/sql.h" -#include "common/strlib.h" -#include "common/timer.h" - -#include - -// Sql settings -static char ipban_db_hostname[32] = "127.0.0.1"; -static uint16 ipban_db_port = 3306; -static char ipban_db_username[32] = "ragnarok"; -static char ipban_db_password[100] = "ragnarok"; -static char ipban_db_database[32] = "ragnarok"; -static char ipban_codepage[32] = ""; -static char ipban_table[32] = "ipbanlist"; - -// globals -static struct Sql *sql_handle = NULL; -static int cleanup_timer_id = INVALID_TIMER; -static bool ipban_inited = false; - -int ipban_cleanup(int tid, int64 tick, int id, intptr_t data); - - -// initialize -void ipban_init(void) -{ - ipban_inited = true; - - if (!login->config->ipban) - return;// ipban disabled - - // establish connections - sql_handle = SQL->Malloc(); - if (SQL_ERROR == SQL->Connect(sql_handle, ipban_db_username, ipban_db_password, - ipban_db_hostname, ipban_db_port, ipban_db_database)) { - Sql_ShowDebug(sql_handle); - SQL->Free(sql_handle); - exit(EXIT_FAILURE); - } - if (ipban_codepage[0] != '\0' && SQL_ERROR == SQL->SetEncoding(sql_handle, ipban_codepage)) - Sql_ShowDebug(sql_handle); - - if (login->config->ipban_cleanup_interval > 0) { - // set up periodic cleanup of connection history and active bans - timer->add_func_list(ipban_cleanup, "ipban_cleanup"); - cleanup_timer_id = timer->add_interval(timer->gettick()+10, ipban_cleanup, 0, 0, login->config->ipban_cleanup_interval*1000); - } else { - // make sure it gets cleaned up on login-server start regardless of interval-based cleanups - ipban_cleanup(0,0,0,0); - } -} - -// finalize -void ipban_final(void) -{ - if (!login->config->ipban) - return;// ipban disabled - - if (login->config->ipban_cleanup_interval > 0) - // release data - timer->delete(cleanup_timer_id, ipban_cleanup); - - ipban_cleanup(0,0,0,0); // always clean up on login-server stop - - // close connections - SQL->Free(sql_handle); - sql_handle = NULL; -} - -/** - * Reads 'inter_configuration' and initializes required variables/Sets global - * configuration. - * - * @param filename Path to configuration file (used in error and warning messages). - * @param imported Whether the current config is imported from another file. - * - * @retval false in case of error. - - */ -bool ipban_config_read_inter(const char *filename, bool imported) -{ - struct config_t config; - struct config_setting_t *setting = NULL; - const char *import = NULL; - bool retval = true; - - nullpo_retr(false, filename); - - if (!libconfig->load_file(&config, filename)) - return false; // Error message is already shown by libconfig->read_file - - if ((setting = libconfig->lookup(&config, "inter_configuration/database_names")) == NULL) { - libconfig->destroy(&config); - if (imported) - return true; - ShowError("ipban_config_read: inter_configuration/database_names was not found!\n"); - return false; - } - libconfig->setting_lookup_mutable_string(setting, "ipban_table", ipban_table, sizeof(ipban_table)); - - // import should overwrite any previous configuration, so it should be called last - if (libconfig->lookup_string(&config, "import", &import) == CONFIG_TRUE) { - if (strcmp(import, filename) == 0 || strcmp(import, "conf/common/inter-server.conf") == 0) { - ShowWarning("ipban_config_read_inter: Loop detected! Skipping 'import'...\n"); - } else { - if (!ipban_config_read_inter(import, true)) - retval = false; - } - } - - libconfig->destroy(&config); - return retval; -} - -/** - * Reads login_configuration/account/ipban/sql_connection and loads configuration options. - * - * @param filename Path to configuration file (used in error and warning messages). - * @param config The current config being parsed. - * @param imported Whether the current config is imported from another file. - * - * @retval false in case of error. - */ -bool ipban_config_read_connection(const char *filename, struct config_t *config, bool imported) -{ - struct config_setting_t *setting = NULL; - - nullpo_retr(false, filename); - nullpo_retr(false, config); - - if ((setting = libconfig->lookup(config, "login_configuration/account/ipban/sql_connection")) == NULL) { - if (imported) - return true; - ShowError("account_db_sql_set_property: login_configuration/account/ipban/sql_connection was not found in %s!\n", filename); - return false; - } - - libconfig->setting_lookup_mutable_string(setting, "db_hostname", ipban_db_hostname, sizeof(ipban_db_hostname)); - libconfig->setting_lookup_mutable_string(setting, "db_database", ipban_db_database, sizeof(ipban_db_database)); - - libconfig->setting_lookup_mutable_string(setting, "db_username", ipban_db_username, sizeof(ipban_db_username)); - libconfig->setting_lookup_mutable_string(setting, "db_password", ipban_db_password, sizeof(ipban_db_password)); - libconfig->setting_lookup_mutable_string(setting, "codepage", ipban_codepage, sizeof(ipban_codepage)); - libconfig->setting_lookup_uint16(setting, "db_port", &ipban_db_port); - - return true; -} - -/** - * Reads login_configuration/account/ipban/dynamic_pass_failure and loads configuration options. - * - * @param filename Path to configuration file (used in error and warning messages). - * @param config The current config being parsed. - * @param imported Whether the current config is imported from another file. - * - * @retval false in case of error. - */ -bool ipban_config_read_dynamic(const char *filename, struct config_t *config, bool imported) -{ - struct config_setting_t *setting = NULL; - - nullpo_retr(false, filename); - nullpo_retr(false, config); - - if ((setting = libconfig->lookup(config, "login_configuration/account/ipban/dynamic_pass_failure")) == NULL) { - if (imported) - return true; - ShowError("account_db_sql_set_property: login_configuration/account/ipban/dynamic_pass_failure was not found in %s!\n", filename); - return false; - } - - libconfig->setting_lookup_bool_real(setting, "enabled", &login->config->dynamic_pass_failure_ban); - libconfig->setting_lookup_uint32(setting, "ban_interval", &login->config->dynamic_pass_failure_ban_interval); - libconfig->setting_lookup_uint32(setting, "ban_limit", &login->config->dynamic_pass_failure_ban_limit); - libconfig->setting_lookup_uint32(setting, "ban_duration", &login->config->dynamic_pass_failure_ban_duration); - - return true; -} - -/** - * Reads login_configuration.account.ipban and loads configuration options. - * - * @param filename Path to configuration file (used in error and warning messages). - * @param config The current config being parsed. - * @param imported Whether the current config is imported from another file. - * - * @retval false in case of error. - */ -bool ipban_config_read(const char *filename, struct config_t *config, bool imported) -{ - struct config_setting_t *setting = NULL; - bool retval = true; - - nullpo_retr(false, filename); - nullpo_retr(false, config); - - if (ipban_inited) - return false; // settings can only be changed before init - - if ((setting = libconfig->lookup(config, "login_configuration/account/ipban")) == NULL) { - if (!imported) - ShowError("login_config_read: login_configuration/log was not found in %s!\n", filename); - return false; - } - - libconfig->setting_lookup_bool_real(setting, "enabled", &login->config->ipban); - libconfig->setting_lookup_uint32(setting, "cleanup_interval", &login->config->ipban_cleanup_interval); - - if (!ipban_config_read_inter("conf/common/inter-server.conf", imported)) - retval = false; - if (!ipban_config_read_connection(filename, config, imported)) - retval = false; - if (!ipban_config_read_dynamic(filename, config, imported)) - retval = false; - - return retval; -} - -// check ip against active bans list -bool ipban_check(uint32 ip) -{ - uint8* p = (uint8*)&ip; - char* data = NULL; - int matches; - - if (!login->config->ipban) - return false;// ipban disabled - - if( SQL_ERROR == SQL->Query(sql_handle, "SELECT count(*) FROM `%s` WHERE `rtime` > NOW() AND (`list` = '%u.*.*.*' OR `list` = '%u.%u.*.*' OR `list` = '%u.%u.%u.*' OR `list` = '%u.%u.%u.%u')", - ipban_table, p[3], p[3], p[2], p[3], p[2], p[1], p[3], p[2], p[1], p[0]) ) - { - Sql_ShowDebug(sql_handle); - // close connection because we can't verify their connectivity. - return true; - } - - if( SQL_SUCCESS != SQL->NextRow(sql_handle) ) - return false; - - SQL->GetData(sql_handle, 0, &data, NULL); - matches = atoi(data); - SQL->FreeResult(sql_handle); - - return( matches > 0 ); -} - -// log failed attempt -void ipban_log(uint32 ip) -{ - unsigned long failures; - - if (!login->config->ipban) - return;// ipban disabled - - failures = loginlog_failedattempts(ip, login->config->dynamic_pass_failure_ban_interval);// how many times failed account? in one ip. - - // if over the limit, add a temporary ban entry - if (failures >= login->config->dynamic_pass_failure_ban_limit) - { - uint8* p = (uint8*)&ip; - if (SQL_ERROR == SQL->Query(sql_handle, "INSERT INTO `%s`(`list`,`btime`,`rtime`,`reason`) VALUES ('%u.%u.%u.*', NOW() , NOW() + INTERVAL %u MINUTE ,'Password error ban')", - ipban_table, p[3], p[2], p[1], login->config->dynamic_pass_failure_ban_duration)) - { - Sql_ShowDebug(sql_handle); - } - } -} - -// remove expired bans -int ipban_cleanup(int tid, int64 tick, int id, intptr_t data) { - if (!login->config->ipban) - return 0;// ipban disabled - - if( SQL_ERROR == SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `rtime` <= NOW()", ipban_table) ) - Sql_ShowDebug(sql_handle); - - return 0; -} diff --git a/src/login/loginlog.c b/src/login/loginlog.c new file mode 100644 index 000000000..7dff14990 --- /dev/null +++ b/src/login/loginlog.c @@ -0,0 +1,225 @@ +/** + * This file is part of Hercules. + * http://herc.ws - http://github.com/HerculesWS/Hercules + * + * Copyright (C) 2012-2016 Hercules Dev Team + * Copyright (C) Athena Dev Teams + * + * Hercules 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. + * + * This program 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 this program. If not, see . + */ +#define HERCULES_CORE + +#include "loginlog.h" + +#include "common/cbasetypes.h" +#include "common/conf.h" +#include "common/mmo.h" +#include "common/nullpo.h" +#include "common/showmsg.h" +#include "common/socket.h" +#include "common/sql.h" +#include "common/strlib.h" + +#include // exit + +// Sql settings +static char log_db_hostname[32] = "127.0.0.1"; +static uint16 log_db_port = 3306; +static char log_db_username[32] = "ragnarok"; +static char log_db_password[100] = "ragnarok"; +static char log_db_database[32] = "ragnarok"; +static char log_codepage[32] = ""; +static char log_login_db[256] = "loginlog"; + +static struct Sql *sql_handle = NULL; +static bool enabled = false; + + +// Returns the number of failed login attempts by the ip in the last minutes. +unsigned long loginlog_failedattempts(uint32 ip, unsigned int minutes) +{ + unsigned long failures = 0; + + if( !enabled ) + return 0; + + if( SQL_ERROR == SQL->Query(sql_handle, "SELECT count(*) FROM `%s` WHERE `ip` = '%s' AND `rcode` = '1' AND `time` > NOW() - INTERVAL %u MINUTE", + log_login_db, sockt->ip2str(ip,NULL), minutes) )// how many times failed account? in one ip. + Sql_ShowDebug(sql_handle); + + if( SQL_SUCCESS == SQL->NextRow(sql_handle) ) + { + char* data; + SQL->GetData(sql_handle, 0, &data, NULL); + failures = strtoul(data, NULL, 10); + SQL->FreeResult(sql_handle); + } + return failures; +} + + +/*============================================= + * Records an event in the login log + *---------------------------------------------*/ +// TODO: add an enum of rcode values +void login_log(uint32 ip, const char* username, int rcode, const char* message) +{ + char esc_username[NAME_LENGTH*2+1]; + char esc_message[255*2+1]; + int retcode; + + nullpo_retv(username); + nullpo_retv(message); + if( !enabled ) + return; + + SQL->EscapeStringLen(sql_handle, esc_username, username, strnlen(username, NAME_LENGTH)); + SQL->EscapeStringLen(sql_handle, esc_message, message, strnlen(message, 255)); + + retcode = SQL->Query(sql_handle, + "INSERT INTO `%s`(`time`,`ip`,`user`,`rcode`,`log`) VALUES (NOW(), '%s', '%s', '%d', '%s')", + log_login_db, sockt->ip2str(ip,NULL), esc_username, rcode, esc_message); + + if( retcode != SQL_SUCCESS ) + Sql_ShowDebug(sql_handle); +} + +bool loginlog_init(void) +{ + sql_handle = SQL->Malloc(); + + if (SQL_ERROR == SQL->Connect(sql_handle, log_db_username, log_db_password, + log_db_hostname, log_db_port, log_db_database)) { + Sql_ShowDebug(sql_handle); + SQL->Free(sql_handle); + exit(EXIT_FAILURE); + } + + if (log_codepage[0] != '\0' && SQL_ERROR == SQL->SetEncoding(sql_handle, log_codepage)) + Sql_ShowDebug(sql_handle); + + enabled = true; + + return true; +} + +bool loginlog_final(void) +{ + SQL->Free(sql_handle); + sql_handle = NULL; + return true; +} + +/** + * Reads 'inter_configuration/database_names' and initializes required + * variables/Sets global configuration. + * + * @param filename Path to configuration file (used in error and warning messages). + * @param config The current config being parsed. + * @param imported Whether the current config is imported from another file. + * + * @retval false in case of error. + */ +bool loginlog_config_read_names(const char *filename, struct config_t *config, bool imported) +{ + struct config_setting_t *setting = NULL; + + nullpo_retr(false, filename); + nullpo_retr(false, config); + + if ((setting = libconfig->lookup(config, "inter_configuration/database_names")) == NULL) { + if (imported) + return true; + ShowError("loginlog_config_read: inter_configuration/database_names was not found in %s!\n", filename); + return false; + } + + libconfig->setting_lookup_mutable_string(setting, "login_db", log_login_db, sizeof(log_login_db)); + + return true; +} + +/** + * Reads 'inter_configuration.log' and initializes required variables/Sets + * global configuration. + * + * @param filename Path to configuration file (used in error and warning messages). + * @param config The current config being parsed. + * @param imported Whether the current config is imported from another file. + * + * @retval false in case of error. + */ +bool loginlog_config_read_log(const char *filename, struct config_t *config, bool imported) +{ + struct config_setting_t *setting = NULL; + + nullpo_retr(false, filename); + nullpo_retr(false, config); + + if ((setting = libconfig->lookup(config, "inter_configuration/log/sql_connection")) == NULL) { + if (imported) + return true; + ShowError("loginlog_config_read: inter_configuration/log/sql_connection was not found in %s!\n", filename); + return false; + } + + libconfig->setting_lookup_mutable_string(setting, "db_hostname", log_db_hostname, sizeof(log_db_hostname)); + libconfig->setting_lookup_mutable_string(setting, "db_database", log_db_database, sizeof(log_db_database)); + libconfig->setting_lookup_mutable_string(setting, "db_username", log_db_username, sizeof(log_db_username)); + libconfig->setting_lookup_mutable_string(setting, "db_password", log_db_password, sizeof(log_db_password)); + + libconfig->setting_lookup_uint16(setting, "db_port", &log_db_port); + libconfig->setting_lookup_mutable_string(setting, "codepage", log_codepage, sizeof(log_codepage)); + + return true; +} + +/** + * Reads 'inter_configuration' and initializes required variables/Sets global + * configuration. + * + * @param filename Path to configuration file. + * @param config The current config being parsed. + * @param imported Whether the current config is imported from another file. + * + * @retval false in case of error. + **/ +bool loginlog_config_read(const char *filename, bool imported) +{ + struct config_t config; + const char *import = NULL; + bool retval = true; + + nullpo_retr(false, filename); + + if (!libconfig->load_file(&config, filename)) + return false; // Error message is already shown by libconfig->load_file + + if (!loginlog_config_read_names(filename, &config, imported)) + retval = false; + if (!loginlog_config_read_log(filename, &config, imported)) + retval = false; + + if (libconfig->lookup_string(&config, "import", &import) == CONFIG_TRUE) { + if (strcmp(import, filename) == 0 || strcmp(import, "conf/common/inter-server.conf") == 0) { + ShowWarning("inter_config_read: Loop detected! Skipping 'import'...\n"); + } else { + if (!loginlog_config_read(import, true)) + retval = false; + } + } + + libconfig->destroy(&config); + return retval; +} diff --git a/src/login/loginlog_sql.c b/src/login/loginlog_sql.c deleted file mode 100644 index 7dff14990..000000000 --- a/src/login/loginlog_sql.c +++ /dev/null @@ -1,225 +0,0 @@ -/** - * This file is part of Hercules. - * http://herc.ws - http://github.com/HerculesWS/Hercules - * - * Copyright (C) 2012-2016 Hercules Dev Team - * Copyright (C) Athena Dev Teams - * - * Hercules 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. - * - * This program 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 this program. If not, see . - */ -#define HERCULES_CORE - -#include "loginlog.h" - -#include "common/cbasetypes.h" -#include "common/conf.h" -#include "common/mmo.h" -#include "common/nullpo.h" -#include "common/showmsg.h" -#include "common/socket.h" -#include "common/sql.h" -#include "common/strlib.h" - -#include // exit - -// Sql settings -static char log_db_hostname[32] = "127.0.0.1"; -static uint16 log_db_port = 3306; -static char log_db_username[32] = "ragnarok"; -static char log_db_password[100] = "ragnarok"; -static char log_db_database[32] = "ragnarok"; -static char log_codepage[32] = ""; -static char log_login_db[256] = "loginlog"; - -static struct Sql *sql_handle = NULL; -static bool enabled = false; - - -// Returns the number of failed login attempts by the ip in the last minutes. -unsigned long loginlog_failedattempts(uint32 ip, unsigned int minutes) -{ - unsigned long failures = 0; - - if( !enabled ) - return 0; - - if( SQL_ERROR == SQL->Query(sql_handle, "SELECT count(*) FROM `%s` WHERE `ip` = '%s' AND `rcode` = '1' AND `time` > NOW() - INTERVAL %u MINUTE", - log_login_db, sockt->ip2str(ip,NULL), minutes) )// how many times failed account? in one ip. - Sql_ShowDebug(sql_handle); - - if( SQL_SUCCESS == SQL->NextRow(sql_handle) ) - { - char* data; - SQL->GetData(sql_handle, 0, &data, NULL); - failures = strtoul(data, NULL, 10); - SQL->FreeResult(sql_handle); - } - return failures; -} - - -/*============================================= - * Records an event in the login log - *---------------------------------------------*/ -// TODO: add an enum of rcode values -void login_log(uint32 ip, const char* username, int rcode, const char* message) -{ - char esc_username[NAME_LENGTH*2+1]; - char esc_message[255*2+1]; - int retcode; - - nullpo_retv(username); - nullpo_retv(message); - if( !enabled ) - return; - - SQL->EscapeStringLen(sql_handle, esc_username, username, strnlen(username, NAME_LENGTH)); - SQL->EscapeStringLen(sql_handle, esc_message, message, strnlen(message, 255)); - - retcode = SQL->Query(sql_handle, - "INSERT INTO `%s`(`time`,`ip`,`user`,`rcode`,`log`) VALUES (NOW(), '%s', '%s', '%d', '%s')", - log_login_db, sockt->ip2str(ip,NULL), esc_username, rcode, esc_message); - - if( retcode != SQL_SUCCESS ) - Sql_ShowDebug(sql_handle); -} - -bool loginlog_init(void) -{ - sql_handle = SQL->Malloc(); - - if (SQL_ERROR == SQL->Connect(sql_handle, log_db_username, log_db_password, - log_db_hostname, log_db_port, log_db_database)) { - Sql_ShowDebug(sql_handle); - SQL->Free(sql_handle); - exit(EXIT_FAILURE); - } - - if (log_codepage[0] != '\0' && SQL_ERROR == SQL->SetEncoding(sql_handle, log_codepage)) - Sql_ShowDebug(sql_handle); - - enabled = true; - - return true; -} - -bool loginlog_final(void) -{ - SQL->Free(sql_handle); - sql_handle = NULL; - return true; -} - -/** - * Reads 'inter_configuration/database_names' and initializes required - * variables/Sets global configuration. - * - * @param filename Path to configuration file (used in error and warning messages). - * @param config The current config being parsed. - * @param imported Whether the current config is imported from another file. - * - * @retval false in case of error. - */ -bool loginlog_config_read_names(const char *filename, struct config_t *config, bool imported) -{ - struct config_setting_t *setting = NULL; - - nullpo_retr(false, filename); - nullpo_retr(false, config); - - if ((setting = libconfig->lookup(config, "inter_configuration/database_names")) == NULL) { - if (imported) - return true; - ShowError("loginlog_config_read: inter_configuration/database_names was not found in %s!\n", filename); - return false; - } - - libconfig->setting_lookup_mutable_string(setting, "login_db", log_login_db, sizeof(log_login_db)); - - return true; -} - -/** - * Reads 'inter_configuration.log' and initializes required variables/Sets - * global configuration. - * - * @param filename Path to configuration file (used in error and warning messages). - * @param config The current config being parsed. - * @param imported Whether the current config is imported from another file. - * - * @retval false in case of error. - */ -bool loginlog_config_read_log(const char *filename, struct config_t *config, bool imported) -{ - struct config_setting_t *setting = NULL; - - nullpo_retr(false, filename); - nullpo_retr(false, config); - - if ((setting = libconfig->lookup(config, "inter_configuration/log/sql_connection")) == NULL) { - if (imported) - return true; - ShowError("loginlog_config_read: inter_configuration/log/sql_connection was not found in %s!\n", filename); - return false; - } - - libconfig->setting_lookup_mutable_string(setting, "db_hostname", log_db_hostname, sizeof(log_db_hostname)); - libconfig->setting_lookup_mutable_string(setting, "db_database", log_db_database, sizeof(log_db_database)); - libconfig->setting_lookup_mutable_string(setting, "db_username", log_db_username, sizeof(log_db_username)); - libconfig->setting_lookup_mutable_string(setting, "db_password", log_db_password, sizeof(log_db_password)); - - libconfig->setting_lookup_uint16(setting, "db_port", &log_db_port); - libconfig->setting_lookup_mutable_string(setting, "codepage", log_codepage, sizeof(log_codepage)); - - return true; -} - -/** - * Reads 'inter_configuration' and initializes required variables/Sets global - * configuration. - * - * @param filename Path to configuration file. - * @param config The current config being parsed. - * @param imported Whether the current config is imported from another file. - * - * @retval false in case of error. - **/ -bool loginlog_config_read(const char *filename, bool imported) -{ - struct config_t config; - const char *import = NULL; - bool retval = true; - - nullpo_retr(false, filename); - - if (!libconfig->load_file(&config, filename)) - return false; // Error message is already shown by libconfig->load_file - - if (!loginlog_config_read_names(filename, &config, imported)) - retval = false; - if (!loginlog_config_read_log(filename, &config, imported)) - retval = false; - - if (libconfig->lookup_string(&config, "import", &import) == CONFIG_TRUE) { - if (strcmp(import, filename) == 0 || strcmp(import, "conf/common/inter-server.conf") == 0) { - ShowWarning("inter_config_read: Loop detected! Skipping 'import'...\n"); - } else { - if (!loginlog_config_read(import, true)) - retval = false; - } - } - - libconfig->destroy(&config); - return retval; -} diff --git a/vcproj-11/login-server.vcxproj b/vcproj-11/login-server.vcxproj index 6e9088f10..9abd5dc90 100644 --- a/vcproj-11/login-server.vcxproj +++ b/vcproj-11/login-server.vcxproj @@ -196,12 +196,12 @@ - + - + - + diff --git a/vcproj-11/login-server.vcxproj.filters b/vcproj-11/login-server.vcxproj.filters index f41fcdfac..1f3b6ba0f 100644 --- a/vcproj-11/login-server.vcxproj.filters +++ b/vcproj-11/login-server.vcxproj.filters @@ -1,19 +1,19 @@  - + login login - + login login - + login diff --git a/vcproj-12/login-server.vcxproj b/vcproj-12/login-server.vcxproj index 91fa58082..a1cd6fc0e 100644 --- a/vcproj-12/login-server.vcxproj +++ b/vcproj-12/login-server.vcxproj @@ -196,12 +196,12 @@ - + - + - + diff --git a/vcproj-12/login-server.vcxproj.filters b/vcproj-12/login-server.vcxproj.filters index f41fcdfac..1f3b6ba0f 100644 --- a/vcproj-12/login-server.vcxproj.filters +++ b/vcproj-12/login-server.vcxproj.filters @@ -1,19 +1,19 @@  - + login login - + login login - + login diff --git a/vcproj-14/login-server.vcxproj b/vcproj-14/login-server.vcxproj index 445d21ee4..44dee52be 100644 --- a/vcproj-14/login-server.vcxproj +++ b/vcproj-14/login-server.vcxproj @@ -194,12 +194,12 @@ - + - + - + diff --git a/vcproj-14/login-server.vcxproj.filters b/vcproj-14/login-server.vcxproj.filters index f41fcdfac..1f3b6ba0f 100644 --- a/vcproj-14/login-server.vcxproj.filters +++ b/vcproj-14/login-server.vcxproj.filters @@ -1,19 +1,19 @@  - + login login - + login login - + login -- cgit v1.2.3-70-g09d2