Browse Source

-Apply login_refac (from old refac branch) in same way as char_refac, each interface now have his own file.
-Remove _sql from some file.
Thx to Euphy for helping with documentation.

lighta 10 years ago
parent
commit
a24da6d302

+ 6 - 0
conf/login_athena.conf

@@ -180,5 +180,11 @@ client_hash_check: off
 //client_hash: 10, cb1ea78023d337c38e8ba5124e2338ae
 //client_hash: 99, disabled
 
+//New registration flood protection [Kevin]
+//Number of new registration allowed
+allowed_regs: 1
+//Time in second before the counter for the number of registration is reset
+time_allowed: 10
+
 import: conf/inter_athena.conf
 import: conf/import/login_conf.txt

+ 2 - 1
conf/subnet_athena.conf

@@ -1,6 +1,7 @@
 // Subnet support file
 // Format is:
 // subnet: net-submask:char_ip:map_ip
-// you can add more than one subnet 
+// you can add more than one subnet (max 16)
+// check is if((net-submask & char_ip ) == (net-submask & servip)) => ok
 
 subnet: 255.0.0.0:127.0.0.1:127.0.0.1

+ 3 - 35
src/char/char.c

@@ -1968,43 +1968,11 @@ int char_lan_subnetcheck(uint32 ip){
 }
 
 // Console Command Parser [Wizputer]
-int parse_console(const char* buf)
-{
-	char type[64];
-	char command[64];
-	int n=0;
-
-	if( ( n = sscanf(buf, "%63[^:]:%63[^\n]", type, command) ) < 2 ){
-		if((n = sscanf(buf, "%63[^\n]", type))<1) return -1; //nothing to do no arg
-	}
-	if( n != 2 ){ //end string
-		ShowNotice("Type: '%s'\n",type);
-		command[0] = '\0';
-	}
-	else
-		ShowNotice("Type of command: '%s' || Command: '%s'\n",type,command);
-
-	if( n == 2 && strcmpi("server", type) == 0 ){
-		if( strcmpi("shutdown", command) == 0 || strcmpi("exit", command) == 0 || strcmpi("quit", command) == 0 ){
-			runflag = CHARSERVER_ST_SHUTDOWN;
-		}
-		else if( strcmpi("alive", command) == 0 || strcmpi("status", command) == 0 )
-			ShowInfo(CL_CYAN"Console: "CL_BOLD"I'm Alive."CL_RESET"\n");
-	}
-	else if( strcmpi("ers_report", type) == 0 ){
-		ers_report();
-	}
-	else if( strcmpi("help", type) == 0 ){
-		ShowInfo("Available commands:\n");
-		ShowInfo("\t server:shutdown => Stops the server.\n");
-		ShowInfo("\t server:alive => Checks if the server is running.\n");
-		ShowInfo("\t ers_report => Displays database usage.\n");
-	}
-
-	return 0;
+//FIXME to be remove (moved to cnslif / will be done once map/char/login, all have their cnslif interface ready)
+int parse_console(const char* buf){
+	return cnslif_parse(buf);
 }
 
-
 //------------------------------------------------
 //Pincode system
 //------------------------------------------------

+ 3 - 0
src/config/const.h

@@ -89,6 +89,9 @@
 /* Feb 1st 2012 */
 #if PACKETVER >= 20120201
 	#define NEW_CARTS
+	#ifndef ENABLE_SC_SAVING
+	 #warning "Cart won't be able to be saved for relog"
+	#endif
 	#define MAX_CARTS 9
 #else
 	#define MAX_CARTS 5

+ 115 - 53
src/login/account_sql.c → src/login/account.c

@@ -1,5 +1,11 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
+/**
+ * @file account.c
+ * Module purpose is to save, load, and update changes into the account table or file.
+ * Licensed under GNU GPL.
+ *  For more information, see LICENCE in the main folder.
+ * @author Athena Dev Teams < r15k
+ * @author rAthena Dev Team
+ */
 
 #include "../common/malloc.h"
 #include "../common/mmo.h"
@@ -16,8 +22,7 @@
 #define ACCOUNT_SQL_DB_VERSION 20110114
 
 /// internal structure
-typedef struct AccountDB_SQL
-{
+typedef struct AccountDB_SQL {
 	AccountDB vtable;    // public interface
 
 	Sql* accounts;       // SQL accounts storage
@@ -44,10 +49,8 @@ typedef struct AccountDB_SQL
 } AccountDB_SQL;
 
 /// internal structure
-typedef struct AccountDBIterator_SQL
-{
+typedef struct AccountDBIterator_SQL {
 	AccountDBIterator vtable;    // public interface
-
 	AccountDB_SQL* db;
 	int last_account_id;
 } AccountDBIterator_SQL;
@@ -70,8 +73,7 @@ static bool mmo_auth_fromsql(AccountDB_SQL* db, struct mmo_account* acc, int acc
 static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, bool is_new);
 
 /// public constructor
-AccountDB* account_db_sql(void)
-{
+AccountDB* account_db_sql(void) {
 	AccountDB_SQL* db = (AccountDB_SQL*)aCalloc(1, sizeof(AccountDB_SQL));
 
 	// set up the vtable
@@ -114,9 +116,11 @@ AccountDB* account_db_sql(void)
 /* ------------------------------------------------------------------------- */
 
 
-/// establishes database connection
-static bool account_db_sql_init(AccountDB* self)
-{
+/**
+ * Establish the database connection.
+ * @param self: pointer to db
+ */
+static bool account_db_sql_init(AccountDB* self) {
 	AccountDB_SQL* db = (AccountDB_SQL*)self;
 	Sql* sql_handle;
 	const char* username;
@@ -162,9 +166,11 @@ static bool account_db_sql_init(AccountDB* self)
 	return true;
 }
 
-/// disconnects from database
-static void account_db_sql_destroy(AccountDB* self)
-{
+/**
+ * Destroy the database and close the connection to it.
+ * @param self: pointer to db
+ */
+static void account_db_sql_destroy(AccountDB* self){
 	AccountDB_SQL* db = (AccountDB_SQL*)self;
 
 	Sql_Free(db->accounts);
@@ -172,7 +178,15 @@ static void account_db_sql_destroy(AccountDB* self)
 	aFree(db);
 }
 
-/// Gets a property from this database.
+/**
+ * Get configuration information into buf.
+ *  If the option is supported, adjust the internal state.
+ * @param self: pointer to db
+ * @param key: config keyword
+ * @param buf: value set of the keyword
+ * @param buflen: size of buffer to avoid out of bound
+ * @return true if successful, false if something has failed
+ */
 static bool account_db_sql_get_property(AccountDB* self, const char* key, char* buf, size_t buflen)
 {
 	AccountDB_SQL* db = (AccountDB_SQL*)self;
@@ -259,9 +273,15 @@ static bool account_db_sql_get_property(AccountDB* self, const char* key, char*
 	return false;// not found
 }
 
-/// if the option is supported, adjusts the internal state
-static bool account_db_sql_set_property(AccountDB* self, const char* key, const char* value)
-{
+/**
+ * Read and set configuration.
+ *  If the option is supported, adjust the internal state.
+ * @param self: pointer to db
+ * @param key: config keyword
+ * @param value: config value for keyword
+ * @return true if successful, false if something has failed
+ */
+static bool account_db_sql_set_property(AccountDB* self, const char* key, const char* value) {
 	AccountDB_SQL* db = (AccountDB_SQL*)self;
 	const char* signature;
 
@@ -330,11 +350,15 @@ static bool account_db_sql_set_property(AccountDB* self, const char* key, const
 	return false;// not found
 }
 
-/// 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)
-{
+/**
+ * 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.
+ * @param self: pointer to db
+ * @param acc: pointer of mmo_account to save
+ * @return true if successful, false if something has failed
+ */
+static bool account_db_sql_create(AccountDB* self, struct mmo_account* acc) {
 	AccountDB_SQL* db = (AccountDB_SQL*)self;
 	Sql* sql_handle = db->accounts;
 
@@ -383,9 +407,13 @@ static bool account_db_sql_create(AccountDB* self, struct mmo_account* acc)
 	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)
-{
+/**
+ * Delete an existing account entry and its regs.
+ * @param self: pointer to db
+ * @param account_id: id of user account
+ * @return true if successful, false if something has failed
+ */
+static bool account_db_sql_remove(AccountDB* self, const int account_id) {
 	AccountDB_SQL* db = (AccountDB_SQL*)self;
 	Sql* sql_handle = db->accounts;
 	bool result = false;
@@ -402,23 +430,40 @@ static bool account_db_sql_remove(AccountDB* self, const int account_id)
 	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)
-{
+/**
+ * Update an existing account with the new data provided (both account and regs).
+ * @param self: pointer to db
+ * @param acc: pointer of mmo_account to save
+ * @return true if successful, false if something has failed
+ */
+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)
-{
+/**
+ * Retrieve data from db and store it in the provided data structure.
+ *  Filled data structure is done by delegation to mmo_auth_fromsql.
+ * @param self: pointer to db
+ * @param acc: pointer of mmo_account to fill
+ * @param account_id: id of user account
+ * @return true if successful, false if something has failed
+ */
+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)
-{
+/**
+ * Retrieve data from db and store it in the provided data structure.
+ *  Doesn't actually retrieve data yet: escapes and checks userid, then transforms it to accid for fetching.
+ *  Filled data structure is done by delegation to account_db_sql_load_num.
+ * @param self: pointer to db
+ * @param acc: pointer of mmo_account to fill
+ * @param userid: name of user account
+ * @return true if successful, false if something has failed
+ */
+static bool account_db_sql_load_str(AccountDB* self, struct mmo_account* acc, const char* userid) {
 	AccountDB_SQL* db = (AccountDB_SQL*)self;
 	Sql* sql_handle = db->accounts;
 	char esc_userid[2*NAME_LENGTH+1];
@@ -454,10 +499,12 @@ static bool account_db_sql_load_str(AccountDB* self, struct mmo_account* acc, co
 	return account_db_sql_load_num(self, acc, account_id);
 }
 
-
-/// Returns a new forward iterator.
-static AccountDBIterator* account_db_sql_iterator(AccountDB* self)
-{
+/**
+ * Create a new forward iterator.
+ * @param self: pointer to db iterator
+ * @return a new db iterator
+ */
+static AccountDBIterator* account_db_sql_iterator(AccountDB* self) {
 	AccountDB_SQL* db = (AccountDB_SQL*)self;
 	AccountDBIterator_SQL* iter = (AccountDBIterator_SQL*)aCalloc(1, sizeof(AccountDBIterator_SQL));
 
@@ -472,18 +519,22 @@ static AccountDBIterator* account_db_sql_iterator(AccountDB* self)
 	return &iter->vtable;
 }
 
-
-/// Destroys this iterator, releasing all allocated memory (including itself).
-static void account_db_sql_iter_destroy(AccountDBIterator* self)
-{
+/**
+ * Destroys this iterator, releasing all allocated memory (including itself).
+ * @param self: pointer to db iterator
+ */
+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)
-{
+/**
+ * Fetches the next account in the database.
+ * @param self: pointer to db iterator
+ * @param acc: pointer of mmo_account to fill
+ * @return true if next account found and filled, false if something has failed
+ */
+static bool account_db_sql_iter_next(AccountDBIterator* self, struct mmo_account* acc) {
 	AccountDBIterator_SQL* iter = (AccountDBIterator_SQL*)self;
 	AccountDB_SQL* db = (AccountDB_SQL*)iter->db;
 	Sql* sql_handle = db->accounts;
@@ -514,9 +565,14 @@ static bool account_db_sql_iter_next(AccountDBIterator* self, struct mmo_account
 	return false;
 }
 
-
-static bool mmo_auth_fromsql(AccountDB_SQL* db, struct mmo_account* acc, int account_id)
-{
+/**
+ * Fetch a struct mmo_account from sql.
+ * @param db: pointer to db
+ * @param acc: pointer of mmo_account to fill
+ * @param account_id: id of user account to take data from
+ * @return true if successful, false if something has failed
+ */
+static bool mmo_auth_fromsql(AccountDB_SQL* db, struct mmo_account* acc, int account_id) {
 	Sql* sql_handle = db->accounts;
 	char* data;
 	int i = 0;
@@ -587,8 +643,14 @@ static bool mmo_auth_fromsql(AccountDB_SQL* db, struct mmo_account* acc, int acc
 	return true;
 }
 
-static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, bool is_new)
-{
+/**
+ * Save a struct mmo_account in sql.
+ * @param db: pointer to db
+ * @param acc: pointer of mmo_account to save
+ * @param is_new: if it's a new entry or should we update
+ * @return true if successful, false if something has failed
+ */
+static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, bool is_new) {
 	Sql* sql_handle = db->accounts;
 	SqlStmt* stmt = SqlStmt_Malloc(sql_handle);
 	bool result = false;

+ 10 - 6
src/login/account.h

@@ -1,5 +1,11 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
+/**
+ * @file account.h
+ * Module purpose is to save, load, and update changes into the account table or file.
+ * Licensed under GNU GPL.
+ *  For more information, see LICENCE in the main folder.
+ * @author Athena Dev Teams < r15k
+ * @author rAthena Dev Team
+ */
 
 #ifndef __ACCOUNT_H_INCLUDED__
 #define __ACCOUNT_H_INCLUDED__
@@ -61,8 +67,7 @@ struct mmo_account {
 };
 
 
-struct AccountDBIterator
-{
+struct AccountDBIterator {
 	/// Destroys this iterator, releasing all allocated memory (including itself).
 	///
 	/// @param self Iterator
@@ -77,8 +82,7 @@ struct AccountDBIterator
 };
 
 
-struct AccountDB
-{
+struct AccountDB {
 	/// Initializes this database, making it ready for use.
 	/// Call this after setting the properties.
 	///

+ 135 - 103
src/login/ipban_sql.c → src/login/ipban.c

@@ -1,5 +1,12 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
+/**
+ * @file ipban.c
+ * Module purpose is to read configuration for login-server and handle accounts,
+ *  and also to synchronize all login interfaces: loginchrif, loginclif, logincnslif.
+ * Licensed under GNU GPL.
+ *  For more information, see LICENCE in the main folder.
+ * @author Athena Dev Teams < r15k
+ * @author rAthena Dev Team
+ */
 
 #include "../common/cbasetypes.h"
 #include "../common/db.h"
@@ -35,82 +42,90 @@ static Sql* sql_handle = NULL;
 static int cleanup_timer_id = INVALID_TIMER;
 static bool ipban_inited = false;
 
+//early declaration
 int ipban_cleanup(int tid, unsigned int tick, int id, intptr_t data);
 
+/**
+ * Check if ip is in the active bans list.
+ * @param ip: ipv4 ip to check if ban
+ * @return true if found or error, false if not in list
+ */
+bool ipban_check(uint32 ip) {
+	uint8* p = (uint8*)&ip;
+	char* data = NULL;
+	int matches;
 
-// initialize
-void ipban_init(void)
-{
-	const char* username;
-	const char* password;
-	const char* hostname;
-	uint16      port;
-	const char* database;
-	const char* codepage;
+	if( !login_config.ipban )
+		return false;// ipban disabled
 
-	ipban_inited = true;
+	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_ERROR == Sql_NextRow(sql_handle) )
+		return true;// Shouldn't happen, but just in case...
+
+	Sql_GetData(sql_handle, 0, &data, NULL);
+	matches = atoi(data);
+	Sql_FreeResult(sql_handle);
+
+	return( matches > 0 );
+}
+
+/**
+ * Log a failed attempt.
+ *  Also bans the user if too many failed attempts are made.
+ * @param ip: ipv4 ip to record the failure
+ */
+void ipban_log(uint32 ip) {
+	unsigned long failures;
 
 	if( !login_config.ipban )
 		return;// ipban disabled
 
-	if( ipban_db_hostname[0] != '\0' )
-	{// local settings
-		username = ipban_db_username;
-		password = ipban_db_password;
-		hostname = ipban_db_hostname;
-		port     = ipban_db_port;
-		database = ipban_db_database;
-		codepage = ipban_codepage;
-	}
-	else
-	{// global settings
-		username = global_db_username;
-		password = global_db_password;
-		hostname = global_db_hostname;
-		port     = global_db_port;
-		database = global_db_database;
-		codepage = global_codepage;
-	}
+	failures = loginlog_failedattempts(ip, login_config.dynamic_pass_failure_ban_interval);// how many times failed account? in one ip.
 
-	// establish connections
-	sql_handle = Sql_Malloc();
-	if( SQL_ERROR == Sql_Connect(sql_handle, username, password, hostname, port, database) )
+	// if over the limit, add a temporary ban entry
+	if( failures >= login_config.dynamic_pass_failure_ban_limit )
 	{
-		Sql_ShowDebug(sql_handle);
-		Sql_Free(sql_handle);
-		exit(EXIT_FAILURE);
+		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 %d MINUTE ,'Password error ban')",
+			ipban_table, p[3], p[2], p[1], login_config.dynamic_pass_failure_ban_duration) )
+			Sql_ShowDebug(sql_handle);
 	}
-	if( codepage[0] != '\0' && SQL_ERROR == Sql_SetEncoding(sql_handle, codepage) )
-		Sql_ShowDebug(sql_handle);
-
-	if( login_config.ipban_cleanup_interval > 0 )
-	{ // set up periodic cleanup of connection history and active bans
-		add_timer_func_list(ipban_cleanup, "ipban_cleanup");
-		cleanup_timer_id = add_timer_interval(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)
-{
+/**
+ * Timered function to remove expired bans.
+ *  Request all characters to update their registered ip and transmit their new ip.
+ *  Performed each ip_sync_interval.
+ * @param tid: timer id
+ * @param tick: tick of execution
+ * @param id: unused
+ * @param data: unused
+ * @return 0
+ */
+int ipban_cleanup(int tid, unsigned int tick, int id, intptr_t data) {
 	if( !login_config.ipban )
-		return;// ipban disabled
+		return 0;// ipban disabled
 
-	if( login_config.ipban_cleanup_interval > 0 )
-		// release data
-		delete_timer(cleanup_timer_id, ipban_cleanup);
-	
-	ipban_cleanup(0,0,0,0); // always clean up on login-server stop
+	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `ipbanlist` WHERE `rtime` <= NOW()") )
+		Sql_ShowDebug(sql_handle);
 
-	// close connections
-	Sql_Free(sql_handle);
-	sql_handle = NULL;
+	return 0;
 }
 
-// load configuration options
-bool ipban_config_read(const char* key, const char* value)
-{
+/**
+ * Read configuration options.
+ * @param key: config keyword
+ * @param value: config value for keyword
+ * @return true if successful, false if config not complete or server already running
+ */
+bool ipban_config_read(const char* key, const char* value) {
 	const char* signature;
 
 	if( ipban_inited )
@@ -197,62 +212,79 @@ bool ipban_config_read(const char* key, const char* value)
 	return false;// not found
 }
 
-// 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
+/// Constructor destructor
 
-	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_ERROR == Sql_NextRow(sql_handle) )
-		return true;// Shouldn't happen, but just in case...
-
-	Sql_GetData(sql_handle, 0, &data, NULL);
-	matches = atoi(data);
-	Sql_FreeResult(sql_handle);
-
-	return( matches > 0 );
-}
+/**
+ * Initialize the module.
+ * Launched at login-serv start, create db or other long scope variable here.
+ */
+void ipban_init(void) {
+	const char* username;
+	const char* password;
+	const char* hostname;
+	uint16      port;
+	const char* database;
+	const char* codepage;
 
-// log failed attempt
-void ipban_log(uint32 ip)
-{
-	unsigned long failures;
+	ipban_inited = true;
 
 	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( ipban_db_hostname[0] != '\0' )
+	{// local settings
+		username = ipban_db_username;
+		password = ipban_db_password;
+		hostname = ipban_db_hostname;
+		port     = ipban_db_port;
+		database = ipban_db_database;
+		codepage = ipban_codepage;
+	}
+	else
+	{// global settings
+		username = global_db_username;
+		password = global_db_password;
+		hostname = global_db_hostname;
+		port     = global_db_port;
+		database = global_db_database;
+		codepage = global_codepage;
+	}
 
-	// if over the limit, add a temporary ban entry
-	if( failures >= login_config.dynamic_pass_failure_ban_limit )
+	// establish connections
+	sql_handle = Sql_Malloc();
+	if( SQL_ERROR == Sql_Connect(sql_handle, username, password, hostname, port, database) )
 	{
-		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 %d MINUTE ,'Password error ban')",
-			ipban_table, p[3], p[2], p[1], login_config.dynamic_pass_failure_ban_duration) )
-			Sql_ShowDebug(sql_handle);
+		Sql_ShowDebug(sql_handle);
+		Sql_Free(sql_handle);
+		exit(EXIT_FAILURE);
 	}
+	if( codepage[0] != '\0' && SQL_ERROR == Sql_SetEncoding(sql_handle, codepage) )
+		Sql_ShowDebug(sql_handle);
+
+	if( login_config.ipban_cleanup_interval > 0 )
+	{ // set up periodic cleanup of connection history and active bans
+		add_timer_func_list(ipban_cleanup, "ipban_cleanup");
+		cleanup_timer_id = add_timer_interval(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);
 }
 
-// remove expired bans
-int ipban_cleanup(int tid, unsigned int tick, int id, intptr_t data)
-{
+/**
+ * Destroy the module.
+ * Launched at login-serv end, cleanup db connection or other thing here.
+ */
+void ipban_final(void) {
 	if( !login_config.ipban )
-		return 0;// ipban disabled
+		return;// ipban disabled
 
-	if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `ipbanlist` WHERE `rtime` <= NOW()") )
-		Sql_ShowDebug(sql_handle);
+	if( login_config.ipban_cleanup_interval > 0 )
+		// release data
+		delete_timer(cleanup_timer_id, ipban_cleanup);
 
-	return 0;
+	ipban_cleanup(0,0,0,0); // always clean up on login-server stop
+
+	// close connections
+	Sql_Free(sql_handle);
+	sql_handle = NULL;
 }

+ 36 - 11
src/login/ipban.h

@@ -1,25 +1,50 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
+/**
+ * @file ipban.h
+ * Module purpose is to read configuration for login-server and handle accounts,
+ *  and also to synchronize all login interfaces: loginchrif, loginclif, logincnslif.
+ * Licensed under GNU GPL.
+ *  For more information, see LICENCE in the main folder.
+ * @author Athena Dev Teams < r15k
+ * @author rAthena Dev Team
+ */
 
 #ifndef __IPBAN_H_INCLUDED__
 #define __IPBAN_H_INCLUDED__
 
 #include "../common/cbasetypes.h"
 
-// initialize
-void ipban_init(void);
-
-// finalize
-void ipban_final(void);
-
-// check ip against ban list
+/**
+ * Check if ip is in the active bans list.
+ * @param ip: ipv4 ip to check if ban
+ * @return true if found or error, false if not in list
+ */
 bool ipban_check(uint32 ip);
 
-// increases failure count for the specified IP
+/**
+ * Log a failed attempt.
+ *  Also bans the user if too many failed attempts are made.
+ * @param ip: ipv4 ip to record the failure
+ */
 void ipban_log(uint32 ip);
 
-// parses configuration option
+/**
+ * Read configuration options.
+ * @param key: config keyword
+ * @param value: config value for keyword
+ * @return true if successful, false if config not complete or server already running
+ */
 bool ipban_config_read(const char* key, const char* value);
 
+/**
+ * Initialize the module.
+ * Launched at login-serv start, create db or other long scope variable here.
+ */
+void ipban_init(void);
+
+/**
+ * Destroy the module.
+ * Launched at login-serv end, cleanup db connection or other thing here.
+ */
+void ipban_final(void);
 
 #endif // __IPBAN_H_INCLUDED__

File diff suppressed because it is too large
+ 116 - 934
src/login/login.c


+ 135 - 15
src/login/login.h

@@ -1,15 +1,22 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
+/**
+ * @file login.h
+ * Module purpose is to read configuration for login-server and handle accounts,
+ *  and also to synchronise all login interfaces: loginchrif, loginclif, logincnslif.
+ * Licensed under GNU GPL.
+ *  For more information, see LICENCE in the main folder.
+ * @author Athena Dev Teams < r15k
+ * @author rAthena Dev Team
+ */
 
 #ifndef _LOGIN_H_
 #define _LOGIN_H_
 
 #include "../common/mmo.h" // NAME_LENGTH,SEX_*
 #include "../common/core.h" // CORE_ST_LAST
+#include "account.h"
 #include "../config/core.h"
 
-enum E_LOGINSERVER_ST
-{
+enum E_LOGINSERVER_ST {
 	LOGINSERVER_ST_RUNNING = CORE_ST_LAST,
 	LOGINSERVER_ST_STARTING,
 	LOGINSERVER_ST_SHUTDOWN,
@@ -27,7 +34,7 @@ struct login_session_data {
 	char sex;			/// 'F','M','S'
 
 	char userid[NAME_LENGTH];	/// account name
-	char passwd[32+1];		/// 23+1 for plaintext, 32+1 for md5-ed passwords
+	char passwd[PASSWD_LENGTH]; // 23+1 for plaintext, 32+1 for md5-ed passwords
 	int passwdenc;			/// was the passwd transmited encrypted or clear ?
 	char md5key[20];		/// md5 key of session (each connection could be encrypted with a md5 key)
 	uint16 md5keylen;		/// len of the md5 key
@@ -43,6 +50,7 @@ struct login_session_data {
 	int fd;				///socket of client
 };
 
+#define MAX_SERVERS 30 //max number of mapserv that could be attach
 ///Struct describing 1 char-serv attach to us
 struct mmo_char_server {
 	char name[20];	///char-serv name
@@ -53,9 +61,10 @@ struct mmo_char_server {
 	uint16 type;	/// 0=normal, 1=maintenance, 2=over 18, 3=paying, 4=P2P
 	uint16 new_;	/// should display as 'new'?
 };
+extern struct mmo_char_server ch_server[MAX_SERVERS];
 
 struct client_hash_node {
-	unsigned int group_id;			///group
+	unsigned int group_id;			//inferior or egal group to apply restriction
 	uint8 hash[16];					///hash required for that groupid or below
 	struct client_hash_node *next;	///next entry
 };
@@ -84,10 +93,16 @@ struct Login_Config {
 	bool use_dnsbl;                                 /// dns blacklist blocking ?
 	char dnsbl_servs[1024];                         /// comma-separated list of dnsbl servers
 
-	char account_engine[256];                       /// name of the engine to use (defaults to auto, for the first available engine)
+	char account_engine[256];				// name of the engine to use (defaults to auto, for the first available engine)
+	int allowed_regs;					//max number of registration
+	int time_allowed;					//registration intervall in seconds
 
-	int client_hash_check;                          /// flags for checking client md5
-	struct client_hash_node *client_hash_nodes;     /// linked list containg md5 hash for each gm group
+	int client_hash_check;					// flags for checking client md5
+	struct client_hash_node *client_hash_nodes;		// linked list containg md5 hash for each gm group
+	char loginconf_name[256];				//name of main config file
+	char msgconf_name[256];					//name of msg_conf config file
+	char lanconf_name[256];					//name of lan config file
+	
 	int char_per_account;                           /// number of characters an account can have
 #ifdef VIP_ENABLE
 	struct {
@@ -96,6 +111,7 @@ struct Login_Config {
 	} vip_sys;
 #endif
 };
+extern struct Login_Config login_config;
 
 #define sex_num2str(num) ( (num ==  SEX_FEMALE  ) ? 'F' : (num ==  SEX_MALE  ) ? 'M' : 'S' )
 #define sex_str2num(str) ( (str == 'F' ) ?  SEX_FEMALE  : (str == 'M' ) ?  SEX_MALE  :  SEX_SERVER  )
@@ -103,15 +119,119 @@ struct Login_Config {
 #define msg_config_read(cfgName) login_msg_config_read(cfgName)
 #define msg_txt(msg_number) login_msg_txt(msg_number)
 #define do_final_msg() login_do_final_msg()
-
 int login_msg_config_read(char *cfgName);
 const char* login_msg_txt(int msg_number);
 void login_do_final_msg(void);
 
-
-#define MAX_SERVERS 30					///number of charserv loginserv can handle
-extern struct mmo_char_server ch_server[MAX_SERVERS];	///array of char-servs data
-extern struct Login_Config login_config;		///config of login serv
-
+/// Online User Database [Wizputer]
+struct online_login_data {
+	int account_id;
+	int waiting_disconnect;
+	int char_server;
+};
+extern DBMap* online_db; // int account_id -> struct online_login_data*
+
+/// Auth database
+#define AUTH_TIMEOUT 30000
+struct auth_node {
+	int account_id;
+	uint32 login_id1;
+	uint32 login_id2;
+	uint32 ip;
+	char sex;
+	uint32 version;
+	uint8 clienttype;
+};
+extern DBMap* auth_db; // int account_id -> struct auth_node*
+
+///Accessors
+AccountDB* login_get_accounts_db(void);
+
+/**
+ * Sub function to create an online_login_data and save it to db.
+ * @param key: Key of the database entry
+ * @param ap: args
+ * @return : Data identified by the key to be put in the database
+ * @see DBCreateData
+ */
+DBData login_create_online_user(DBKey key, va_list args);
+
+/**
+ * Function to add a user in online_db.
+ *  Checking if the user is already registered in the db.
+ *  Stop disconnection timer if set.
+ * @param char_server: id of char-serv on wich the player is
+ * @param account_id: the account identifier
+ * @return the new|registered online data
+ */
+struct online_login_data* login_add_online_user(int char_server, int account_id);
+
+/**
+ * Function to remove a user from online_db.
+ *  Checking if user was already scheduled for deletion, and remove that timer if found.
+ * @param account_id: the account identifier
+ */
+void login_remove_online_user(int account_id);
+
+/**
+ * Timered function to disconnect a user from login.
+ *  This is done either after auth_ok or kicked by char-server.
+ *  Removing user from auth_db and online_db.
+ *  Delay is AUTH_TIMEOUT by default.
+ * @param tid: timer id
+ * @param tick: tick of execution
+ * @param id: user account id
+ * @param data: unused
+ * @return :0
+ */
+int login_waiting_disconnect_timer(int tid, unsigned int tick, int id, intptr_t data);
+
+/**
+ * Sub function to apply on online_db.
+ * Mark a character as offline.
+ * @param data: 1 entry in the db
+ * @param ap: args
+ * @return : Value to be added up by the function that is applying this
+ * @see DBApply
+ */
+int login_online_db_setoffline(DBKey key, DBData *data, va_list ap);
+
+/**
+ * Test to determine if an IP come from LAN or WAN.
+ * @param ip: ip to check if in auth network
+ * @return 0 if from wan, or subnet_char_ip if lan
+ */
+int lan_subnetcheck(uint32 ip);
+
+
+/**
+ * Create a new account and save it in db/sql.
+ * @param userid: string for user login
+ * @param pass: string for user pass
+ * @param sex: should be M|F|S (todo make an enum ?)
+ * @param last_ip:
+ * @return :
+ *	-1: success
+ *	0: unregistered id (wrong sex fail to create in db);
+ *	1: incorrect pass or userid (userid|pass too short or already exist);
+ *	3: registration limit exceeded;
+ */
+int login_mmo_auth_new(const char* userid, const char* pass, const char sex, const char* last_ip);
+
+/**
+ * Check/authentication of a connection.
+ * @param sd: string (atm:md5key or dbpass)
+ * @param isServer: string (atm:md5key or dbpass)
+ * @return :
+ *	-1: success
+ *	0: unregistered id;
+ *	1: incorrect pass;
+ *	2: expired id
+ *	3: blacklisted (or registration limit exceeded if new acc);
+ *	5: invalid client_version|hash;
+ *	6: banned
+ *	x: acc state (TODO document me deeper)
+ */
+int login_mmo_auth(struct login_session_data* sd, bool isServer);
 
 #endif /* _LOGIN_H_ */

+ 967 - 0
src/login/loginchrif.c

@@ -0,0 +1,967 @@
+/**
+ * @file loginchrif.c
+ * Module purpose is to handle incoming and outgoing requests with char-server.
+ * Licensed under GNU GPL.
+ *  For more information, see LICENCE in the main folder.
+ * @author Athena Dev Teams originally in login.c
+ * @author rAthena Dev Team
+ */
+
+#include "../common/timer.h" //difftick
+#include "../common/strlib.h" //safeprint
+#include "../common/showmsg.h" //show notice
+#include "../common/socket.h" //wfifo session
+#include "../common/malloc.h"
+#include "account.h"
+#include "ipban.h" //ipban_check
+#include "login.h"
+#include "loginlog.h"
+#include "loginclif.h"
+#include "loginchrif.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+//early declaration
+void logchrif_on_disconnect(int id);
+
+/**
+ * Packet send to all char-servers, except one. (wos: without our self)
+ * @param sfd: fd to discard sending to
+ * @param buf: packet to send in form of an array buffer
+ * @param len: size of packet
+ * @return : the number of char-serv the packet was sent to
+ */
+int logchrif_sendallwos(int sfd, uint8* buf, size_t len) {
+	int i, c;
+	for( i = 0, c = 0; i < ARRAYLENGTH(ch_server); ++i ) {
+		int fd = ch_server[i].fd;
+		if( session_isValid(fd) && fd != sfd ){
+			WFIFOHEAD(fd,len);
+			memcpy(WFIFOP(fd,0), buf, len);
+			WFIFOSET(fd,len);
+			++c;
+		}
+	}
+	return c;
+}
+
+/**
+ * Timered function to synchronize ip addresses.
+ *  Requesting all char to update their registered ip and transmit their new ip.
+ *  Performed each ip_sync_interval.
+ * @param tid: timer id
+ * @param tick: tick of execution
+ * @param id: unused
+ * @param data: unused
+ * @return 0
+ */
+static int logchrif_sync_ip_addresses(int tid, unsigned int tick, int id, intptr_t data) {
+	uint8 buf[2];
+	ShowInfo("IP Sync in progress...\n");
+	WBUFW(buf,0) = 0x2735;
+	logchrif_sendallwos(-1, buf, 2);
+	return 0;
+}
+
+
+
+
+/// Parsing handlers
+
+/**
+ * Request from char-server to authenticate an account.
+ * @param fd: fd to parse from (char-serv)
+ * @param id: id of char-serv
+ * @param ip: char-serv ip (used for info)
+ * @return 0 not enough info transmitted, 1 success
+ */
+int logchrif_parse_reqauth(int fd, int id,char* ip){
+	if( RFIFOREST(fd) < 23 )
+		return 0;
+	else{
+		struct auth_node* node;
+		int account_id = RFIFOL(fd,2);
+		uint32 login_id1 = RFIFOL(fd,6);
+		uint32 login_id2 = RFIFOL(fd,10);
+		uint8 sex = RFIFOB(fd,14);
+		//uint32 ip_ = ntohl(RFIFOL(fd,15));
+		int request_id = RFIFOL(fd,19);
+		RFIFOSKIP(fd,23);
+
+		node = (struct auth_node*)idb_get(auth_db, account_id);
+		if( runflag == LOGINSERVER_ST_RUNNING &&
+			node != NULL &&
+			node->account_id == account_id &&
+			node->login_id1  == login_id1 &&
+			node->login_id2  == login_id2 &&
+			node->sex        == sex_num2str(sex) /*&&
+			node->ip         == ip_*/ ){// found
+			//ShowStatus("Char-server '%s': authentication of the account %d accepted (ip: %s).\n", server[id].name, account_id, ip);
+
+			// send ack
+			WFIFOHEAD(fd,25);
+			WFIFOW(fd,0) = 0x2713;
+			WFIFOL(fd,2) = account_id;
+			WFIFOL(fd,6) = login_id1;
+			WFIFOL(fd,10) = login_id2;
+			WFIFOB(fd,14) = sex;
+			WFIFOB(fd,15) = 0;// ok
+			WFIFOL(fd,16) = request_id;
+			WFIFOL(fd,20) = node->version;
+			WFIFOB(fd,24) = node->clienttype;
+			WFIFOSET(fd,25);
+
+			// each auth entry can only be used once
+			idb_remove(auth_db, account_id);
+		}else{// authentication not found
+			ShowStatus("Char-server '%s': authentication of the account %d REFUSED (ip: %s).\n", ch_server[id].name, account_id, ip);
+			WFIFOHEAD(fd,25);
+			WFIFOW(fd,0) = 0x2713;
+			WFIFOL(fd,2) = account_id;
+			WFIFOL(fd,6) = login_id1;
+			WFIFOL(fd,10) = login_id2;
+			WFIFOB(fd,14) = sex;
+			WFIFOB(fd,15) = 1;// auth failed
+			WFIFOL(fd,16) = request_id;
+			WFIFOL(fd,20) = 0;
+			WFIFOB(fd,24) = 0;
+			WFIFOSET(fd,25);
+		}
+	}
+	return 1;
+}
+
+/**
+ * Receive a request to update user count for char-server identified by id.
+ * @param fd: fd to parse from (char-serv)
+ * @param id: id of char-serv
+ * @return 0 not enough info transmitted, 1 success
+ */
+int logchrif_parse_ackusercount(int fd, int id){
+	if( RFIFOREST(fd) < 6 )
+		return 0;
+	else{
+		int users = RFIFOL(fd,2);
+		RFIFOSKIP(fd,6);
+		// how many users on world? (update)
+		if( ch_server[id].users != users ){
+			ShowStatus("set users %s : %d\n", ch_server[id].name, users);
+			ch_server[id].users = users;
+		}
+	}
+	return 1;
+}
+
+/**
+ * Receive a request from char-server to change e-mail from default "a@a.com".
+ * @param fd: fd to parse from (char-serv)
+ * @param id: id of char-serv
+ * @param ip: char-serv ip (used for info)
+ * @return 0 not enough info transmitted, 1 success
+ */
+int logchrif_parse_updmail(int fd, int id, char* ip){
+	if (RFIFOREST(fd) < 46)
+		return 0;
+	else{
+		AccountDB* accounts = login_get_accounts_db();
+		struct mmo_account acc;
+		char email[40];
+
+		int account_id = RFIFOL(fd,2);
+		safestrncpy(email, (char*)RFIFOP(fd,6), 40); remove_control_chars(email);
+		RFIFOSKIP(fd,46);
+
+		if( e_mail_check(email) == 0 )
+			ShowNotice("Char-server '%s': Attempt to create an e-mail on an account with a default e-mail REFUSED - e-mail is invalid (account: %d, ip: %s)\n", ch_server[id].name, account_id, ip);
+		else if( !accounts->load_num(accounts, &acc, account_id) || strcmp(acc.email, "a@a.com") == 0 || acc.email[0] == '\0' )
+			ShowNotice("Char-server '%s': Attempt to create an e-mail on an account with a default e-mail REFUSED - account doesn't exist or e-mail of account isn't default e-mail (account: %d, ip: %s).\n", ch_server[id].name, account_id, ip);
+		else{
+			memcpy(acc.email, email, 40);
+			ShowNotice("Char-server '%s': Create an e-mail on an account with a default e-mail (account: %d, new e-mail: %s, ip: %s).\n", ch_server[id].name, account_id, email, ip);
+			// Save
+			accounts->save(accounts, &acc);
+		}
+	}
+	return 1;
+}
+
+/**
+ * Transmit account data to char_server
+ * S 2717 aid.W email.40B exp_time.L group_id.B char_slot.B birthdate.11B pincode.5B pincode_change.L bank_vault.L
+ *  isvip.1B char_vip.1B max_billing.1B (tot 79)  
+ * @return -1 : account not found, 1:sucess
+ */
+int logchrif_send_accdata(int fd, uint32 aid) {
+	struct mmo_account acc;
+	time_t expiration_time = 0;
+	char email[40] = "";
+	int group_id = 0;
+	char birthdate[10+1] = "";
+	char pincode[PINCODE_LENGTH+1];
+	int bank_vault = 0;
+	char isvip = false;
+	uint8 char_slots = MIN_CHARS, char_vip = 0;
+
+	memset(pincode,0,PINCODE_LENGTH+1);
+	AccountDB* accounts = login_get_accounts_db();
+	if( !accounts->load_num(accounts, &acc, aid) )
+		return -1;
+	else {
+		safestrncpy(email, acc.email, sizeof(email));
+		expiration_time = acc.expiration_time;
+		group_id = acc.group_id;
+
+		safestrncpy(birthdate, acc.birthdate, sizeof(birthdate));
+		safestrncpy(pincode, acc.pincode, sizeof(pincode));
+		bank_vault = acc.bank_vault;
+#ifdef VIP_ENABLE
+		char_vip = login_config.vip_sys.char_increase;
+		if( acc.vip_time > time(NULL) ) {
+			isvip = true;
+			char_slots = login_config.char_per_account + char_vip;
+		} else
+			char_slots = login_config.char_per_account;
+#endif
+	}
+
+	WFIFOHEAD(fd,79);
+	WFIFOW(fd,0) = 0x2717;
+	WFIFOL(fd,2) = aid;
+	safestrncpy((char*)WFIFOP(fd,6), email, 40);
+	WFIFOL(fd,46) = (uint32)expiration_time;
+	WFIFOB(fd,50) = (unsigned char)group_id;
+	WFIFOB(fd,51) = char_slots;
+	safestrncpy((char*)WFIFOP(fd,52), birthdate, 10+1);
+	safestrncpy((char*)WFIFOP(fd,63), pincode, 4+1 );
+	WFIFOL(fd,68) = (uint32)acc.pincode_change;
+	WFIFOL(fd,72) = bank_vault;
+	WFIFOB(fd,76) = isvip;
+	WFIFOB(fd,77) = char_vip;
+	WFIFOB(fd,78) = MAX_CHAR_BILLING; //TODO create a config for this
+	WFIFOSET(fd,79);
+	return 1;
+}
+
+/**
+ * Transmit vip specific data to char-serv (will be transfered to mapserv)
+ */
+int logchrif_sendvipdata(int fd, struct mmo_account acc, char isvip, char isgm, int mapfd) {
+#ifdef VIP_ENABLE
+	WFIFOHEAD(fd,19);
+	WFIFOW(fd,0) = 0x2743;
+	WFIFOL(fd,2) = acc.account_id;
+	WFIFOL(fd,6) = (int)acc.vip_time;
+	WFIFOB(fd,10) = isvip;
+	WFIFOL(fd,11) = acc.group_id; //new group id
+	WFIFOL(fd,15) = isgm;
+	WFIFOL(fd,16) = mapfd; //link to mapserv
+	WFIFOSET(fd,20);
+	chrif_send_accdata(fd,acc.account_id); //refresh char with new setting
+#endif
+	return 1;
+}
+
+/**
+ * Receive a request for account data reply by sending all mmo_account information.
+ * @param fd: fd to parse from (char-serv)
+ * @param id: id of char-serv
+ * @param ip: char-serv ip (used for info)
+ * @return 0 not enough info transmitted, 1 success
+ */
+int logchrif_parse_reqaccdata(int fd, int id, char *ip){
+	if( RFIFOREST(fd) < 6 )
+		return 0;
+	else {
+		uint32 aid = RFIFOL(fd,2);
+		RFIFOSKIP(fd,6);
+		if( logchrif_send_accdata(fd,aid) < 0 )
+			ShowNotice("Char-server '%s': account %d NOT found (ip: %s).\n", ch_server[id].name, aid, ip);
+	}
+	return 1;
+}
+
+/**
+ * Ping request from char-server to send a reply.
+ * @param fd: fd to parse from (char-serv)
+ * @return 1 success
+ */
+int logchrif_parse_keepalive(int fd){
+	RFIFOSKIP(fd,2);
+	WFIFOHEAD(fd,2);
+	WFIFOW(fd,0) = 0x2718;
+	WFIFOSET(fd,2);
+	return 1;
+}
+
+/**
+ * Map server send information to change an email of an account via char-server.
+ * 0x2722 <account_id>.L <actual_e-mail>.40B <new_e-mail>.40B
+ * @param fd: fd to parse from (char-serv)
+ * @param id: id of char-serv
+ * @param ip: char-serv ip (used for info)
+ * @return 0 not enough info transmitted, 1 success
+ */
+int logchrif_parse_reqchangemail(int fd, int id, char* ip){
+	if (RFIFOREST(fd) < 86)
+		return 0;
+	else{
+		struct mmo_account acc;
+		AccountDB* accounts = login_get_accounts_db();
+		char actual_email[40];
+		char new_email[40];
+
+		int account_id = RFIFOL(fd,2);
+		safestrncpy(actual_email, (char*)RFIFOP(fd,6), 40);
+		safestrncpy(new_email, (char*)RFIFOP(fd,46), 40);
+		RFIFOSKIP(fd, 86);
+
+		if( e_mail_check(actual_email) == 0 )
+			ShowNotice("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command), but actual email is invalid (account: %d, ip: %s)\n", ch_server[id].name, account_id, ip);
+		else if( e_mail_check(new_email) == 0 )
+			ShowNotice("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command) with a invalid new e-mail (account: %d, ip: %s)\n", ch_server[id].name, account_id, ip);
+		else if( strcmpi(new_email, "a@a.com") == 0 )
+			ShowNotice("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command) with a default e-mail (account: %d, ip: %s)\n", ch_server[id].name, account_id, ip);
+		else if( !accounts->load_num(accounts, &acc, account_id) )
+			ShowNotice("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command), but account doesn't exist (account: %d, ip: %s).\n", ch_server[id].name, account_id, ip);
+		else if( strcmpi(acc.email, actual_email) != 0 )
+			ShowNotice("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command), but actual e-mail is incorrect (account: %d (%s), actual e-mail: %s, proposed e-mail: %s, ip: %s).\n", ch_server[id].name, account_id, acc.userid, acc.email, actual_email, ip);
+		else{
+			safestrncpy(acc.email, new_email, 40);
+			ShowNotice("Char-server '%s': Modify an e-mail on an account (@email GM command) (account: %d (%s), new e-mail: %s, ip: %s).\n", ch_server[id].name, account_id, acc.userid, new_email, ip);
+			// Save
+			accounts->save(accounts, &acc);
+		}
+	}
+	return 1;
+}
+
+/**
+ * Receiving an account state update request from a map-server (relayed via char-server).
+ * @param fd: fd to parse from (char-serv)
+ * @param id: id of char-serv
+ * @param ip: char-serv ip (used for info)
+ * @return 0 not enough info transmitted, 1 success
+ * TODO seems pretty damn close to logchrif_parse_reqbanacc
+ */
+int logchrif_parse_requpdaccstate(int fd, int id, char* ip){
+	if (RFIFOREST(fd) < 10)
+		return 0;
+	else{
+		struct mmo_account acc;
+
+		int account_id = RFIFOL(fd,2);
+		unsigned int state = RFIFOL(fd,6);
+		RFIFOSKIP(fd,10);
+
+		AccountDB* accounts = login_get_accounts_db();
+		if( !accounts->load_num(accounts, &acc, account_id) )
+			ShowNotice("Char-server '%s': Error of Status change (account: %d not found, suggested status %d, ip: %s).\n", ch_server[id].name, account_id, state, ip);
+		else if( acc.state == state )
+			ShowNotice("Char-server '%s':  Error of Status change - actual status is already the good status (account: %d, status %d, ip: %s).\n", ch_server[id].name, account_id, state, ip);
+		else{
+			ShowNotice("Char-server '%s': Status change (account: %d, new status %d, ip: %s).\n", ch_server[id].name, account_id, state, ip);
+
+			acc.state = state;
+			// Save
+			accounts->save(accounts, &acc);
+
+			// notify other servers
+			if (state != 0){
+				uint8 buf[11];
+				WBUFW(buf,0) = 0x2731;
+				WBUFL(buf,2) = account_id;
+				WBUFB(buf,6) = 0; // 0: change of state, 1: ban
+				WBUFL(buf,7) = state; // status or final date of a banishment
+				logchrif_sendallwos(-1, buf, 11);
+			}
+		}
+	}
+	return 1;
+}
+
+/**
+ * Receiving a ban request from map-server via char-server.
+ * @param fd: fd to parse from (char-serv)
+ * @param id: id of char-serv
+ * @param ip: char-serv ip (used for info)
+ * @return 0 not enough info transmitted, 1 success
+ * TODO check logchrif_parse_requpdaccstate for possible merge
+ */
+int logchrif_parse_reqbanacc(int fd, int id, char* ip){
+	if (RFIFOREST(fd) < 10)
+		return 0;
+	else{
+		struct mmo_account acc;
+		AccountDB* accounts = login_get_accounts_db();
+
+		int account_id = RFIFOL(fd,2);
+		int timediff = RFIFOL(fd,6);
+		RFIFOSKIP(fd,10);
+
+		if( !accounts->load_num(accounts, &acc, account_id) )
+			ShowNotice("Char-server '%s': Error of ban request (account: %d not found, ip: %s).\n", ch_server[id].name, account_id, ip);
+		else{
+			time_t timestamp;
+			if (acc.unban_time == 0 || acc.unban_time < time(NULL))
+				timestamp = time(NULL); // new ban
+			else
+				timestamp = acc.unban_time; // add to existing ban
+			timestamp += timediff;
+			if (timestamp == -1)
+				ShowNotice("Char-server '%s': Error of ban request (account: %d, invalid date, ip: %s).\n", ch_server[id].name, account_id, ip);
+			else if( timestamp <= time(NULL) || timestamp == 0 )
+				ShowNotice("Char-server '%s': Error of ban request (account: %d, new date unbans the account, ip: %s).\n", ch_server[id].name, account_id, ip);
+			else{
+				uint8 buf[11];
+				char tmpstr[24];
+				timestamp2string(tmpstr, sizeof(tmpstr), timestamp, login_config.date_format);
+				ShowNotice("Char-server '%s': Ban request (account: %d, new final date of banishment: %d (%s), ip: %s).\n", ch_server[id].name, account_id, timestamp, tmpstr, ip);
+
+				acc.unban_time = timestamp;
+
+				// Save
+				accounts->save(accounts, &acc);
+
+				WBUFW(buf,0) = 0x2731;
+				WBUFL(buf,2) = account_id;
+				WBUFB(buf,6) = 1; // 0: change of status, 1: ban
+				WBUFL(buf,7) = (uint32)timestamp; // status or final date of a banishment
+				logchrif_sendallwos(-1, buf, 11);
+			}
+		}
+	}
+	return 1;
+}
+
+/**
+ * Receiving a sex change request (sex is reversed).
+ * @param fd: fd to parse from (char-serv)
+ * @param id: id of char-serv
+ * @param ip: char-serv ip (used for info)
+ * @return 0 not enough info transmitted, 1 success
+ */
+int logchrif_parse_reqchgsex(int fd, int id, char* ip){
+	if( RFIFOREST(fd) < 6 )
+		return 0;
+	else{
+		struct mmo_account acc;
+		AccountDB* accounts = login_get_accounts_db();
+
+		int account_id = RFIFOL(fd,2);
+		RFIFOSKIP(fd,6);
+
+		if( !accounts->load_num(accounts, &acc, account_id) )
+			ShowNotice("Char-server '%s': Error of sex change (account: %d not found, ip: %s).\n", ch_server[id].name, account_id, ip);
+		else if( acc.sex == 'S' )
+			ShowNotice("Char-server '%s': Error of sex change - account to change is a Server account (account: %d, ip: %s).\n", ch_server[id].name, account_id, ip);
+		else{
+			unsigned char buf[7];
+			char sex = ( acc.sex == 'M' ) ? 'F' : 'M'; //Change gender
+
+			ShowNotice("Char-server '%s': Sex change (account: %d, new sex %c, ip: %s).\n", ch_server[id].name, account_id, sex, ip);
+
+			acc.sex = sex;
+			// Save
+			accounts->save(accounts, &acc);
+
+			// announce to other servers
+			WBUFW(buf,0) = 0x2723;
+			WBUFL(buf,2) = account_id;
+			WBUFB(buf,6) = sex_str2num(sex);
+			logchrif_sendallwos(-1, buf, 7);
+		}
+	}
+	return 1;
+}
+
+/**
+ * We receive account_reg2 from a char-server, and we send them to other char-servers.
+ * @param fd: fd to parse from (char-serv)
+ * @param id: id of char-serv
+ * @param ip: char-serv ip (used for info)
+ * @return 0 not enough info transmitted, 1 success
+ */
+int logchrif_parse_updreg2(int fd, int id, char* ip){
+	int j;
+	if( RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2) )
+		return 0;
+	else{
+		struct mmo_account acc;
+		AccountDB* accounts = login_get_accounts_db();
+		int account_id = RFIFOL(fd,4);
+
+		if( !accounts->load_num(accounts, &acc, account_id) )
+			ShowStatus("Char-server '%s': receiving (from the char-server) of account_reg2 (account: %d not found, ip: %s).\n", ch_server[id].name, account_id, ip);
+		else{
+			int len;
+			int p;
+			ShowNotice("char-server '%s': receiving (from the char-server) of account_reg2 (account: %d, ip: %s).\n", ch_server[id].name, account_id, ip);
+			for( j = 0, p = 13; j < ACCOUNT_REG2_NUM && p < RFIFOW(fd,2); ++j ){
+				sscanf((char*)RFIFOP(fd,p), "%31c%n", acc.account_reg2[j].str, &len);
+				acc.account_reg2[j].str[len]='\0';
+				p +=len+1; //+1 to skip the '\0' between strings.
+				sscanf((char*)RFIFOP(fd,p), "%255c%n", acc.account_reg2[j].value, &len);
+				acc.account_reg2[j].value[len]='\0';
+				p +=len+1;
+				remove_control_chars(acc.account_reg2[j].str);
+				remove_control_chars(acc.account_reg2[j].value);
+			}
+			acc.account_reg2_num = j;
+			// Save
+			accounts->save(accounts, &acc);
+			// Sending information towards the other char-servers.
+			RFIFOW(fd,0) = 0x2729;// reusing read buffer
+			logchrif_sendallwos(fd, RFIFOP(fd,0), RFIFOW(fd,2));
+		}
+		RFIFOSKIP(fd,RFIFOW(fd,2));
+	}
+	return 1;
+}
+
+/**
+ * Receiving an unban request from map-server via char-server.
+ * @param fd: fd to parse from (char-serv)
+ * @param id: id of char-serv
+ * @param ip: char-serv ip (used for info)
+ * @return 0 not enough info transmitted, 1 success
+ */
+int logchrif_parse_requnbanacc(int fd, int id, char* ip){
+	if( RFIFOREST(fd) < 6 )
+		return 0;
+	else{
+		struct mmo_account acc;
+		AccountDB* accounts = login_get_accounts_db();
+
+		int account_id = RFIFOL(fd,2);
+		RFIFOSKIP(fd,6);
+
+		if( !accounts->load_num(accounts, &acc, account_id) )
+			ShowNotice("Char-server '%s': Error of UnBan request (account: %d not found, ip: %s).\n", ch_server[id].name, account_id, ip);
+		else if( acc.unban_time == 0 )
+			ShowNotice("Char-server '%s': Error of UnBan request (account: %d, no change for unban date, ip: %s).\n", ch_server[id].name, account_id, ip);
+		else{
+			ShowNotice("Char-server '%s': UnBan request (account: %d, ip: %s).\n", ch_server[id].name, account_id, ip);
+			acc.unban_time = 0;
+			accounts->save(accounts, &acc);
+		}
+	}
+	return 1;
+}
+
+/**
+ * Set account_id to online.
+ * @author [Wizputer]
+ * @param fd: fd to parse from (char-serv)
+ * @param id: id of char-serv
+ * @return 0 not enough info transmitted, 1 success
+ */
+int logchrif_parse_setacconline(int fd, int id){
+	if( RFIFOREST(fd) < 6 )
+		return 0;
+	login_add_online_user(id, RFIFOL(fd,2));
+	RFIFOSKIP(fd,6);
+	return 1;
+}
+
+/**
+ * Set account_id to offline.
+ * @author  [Wizputer]
+ * @param fd: fd to parse from (char-serv)
+ * @return 0 not enough info transmitted, 1 success
+ */
+int logchrif_parse_setaccoffline(int fd){
+	if( RFIFOREST(fd) < 6 )
+		return 0;
+	login_remove_online_user(RFIFOL(fd,2));
+	RFIFOSKIP(fd,6);
+	return 1;
+}
+
+/**
+ * Receive list of all online accounts.
+ * @author  [Skotlex]
+ * @param fd: fd to parse from (char-serv)
+ * @param id: id of char-serv
+ * @return 0 not enough info transmitted, 1 success
+ */
+int logchrif_parse_updonlinedb(int fd, int id){
+	if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2))
+		return 0;
+	else{
+		uint32 i, users;
+		online_db->foreach(online_db, login_online_db_setoffline, id); //Set all chars from this char-server offline first
+		users = RFIFOW(fd,4);
+		for (i = 0; i < users; i++) {
+			int aid = RFIFOL(fd,6+i*4);
+			struct online_login_data *p = idb_ensure(online_db, aid, login_create_online_user);
+			p->char_server = id;
+			if (p->waiting_disconnect != INVALID_TIMER){
+				delete_timer(p->waiting_disconnect, login_waiting_disconnect_timer);
+				p->waiting_disconnect = INVALID_TIMER;
+			}
+		}
+		RFIFOSKIP(fd,RFIFOW(fd,2));
+	}
+	return 1;
+}
+
+/**
+ * Request account_reg2 for a character.
+ * @param fd: fd to parse from (char-serv)
+ * @return 0 not enough info transmitted, 1 success
+ */
+int logchrif_parse_reqacc2reg(int fd){
+	int j;
+	if (RFIFOREST(fd) < 10)
+		return 0;
+	else{
+		struct mmo_account acc;
+		AccountDB* accounts = login_get_accounts_db();
+		size_t off;
+
+		int account_id = RFIFOL(fd,2);
+		int char_id = RFIFOL(fd,6);
+		RFIFOSKIP(fd,10);
+
+		WFIFOHEAD(fd,ACCOUNT_REG2_NUM*sizeof(struct global_reg));
+		WFIFOW(fd,0) = 0x2729;
+		WFIFOL(fd,4) = account_id;
+		WFIFOL(fd,8) = char_id;
+		WFIFOB(fd,12) = 1; //Type 1 for Account2 registry
+
+		off = 13;
+		if( accounts->load_num(accounts, &acc, account_id) ){
+			for( j = 0; j < acc.account_reg2_num; j++ ){
+				if( acc.account_reg2[j].str[0] != '\0' ){
+					off += sprintf((char*)WFIFOP(fd,off), "%s", acc.account_reg2[j].str)+1; //We add 1 to consider the '\0' in place.
+					off += sprintf((char*)WFIFOP(fd,off), "%s", acc.account_reg2[j].value)+1;
+				}
+			}
+		}
+
+		WFIFOW(fd,2) = (uint16)off;
+		WFIFOSET(fd,WFIFOW(fd,2));
+	}
+	return 1;
+}
+
+/**
+ * Received new charip from char-serv, update information.
+ * @param fd: char-serv file descriptor
+ * @param id: char-serv id
+ * @return 0 not enough info transmitted, 1 success
+ */
+int logchrif_parse_updcharip(int fd, int id){
+	if( RFIFOREST(fd) < 6 )
+		return 0;
+	ch_server[id].ip = ntohl(RFIFOL(fd,2));
+	ShowInfo("Updated IP of Server #%d to %d.%d.%d.%d.\n",id, CONVIP(ch_server[id].ip));
+	RFIFOSKIP(fd,6);
+	return 1;
+}
+
+/**
+ * Request to set all accounts offline.
+ * @param fd: fd to parse from (char-serv)
+ * @param id: id of char-serv (char-serv)
+ * @return 1 success
+ */
+int logchrif_parse_setalloffline(int fd, int id){
+	ShowInfo("Setting accounts from char-server %d offline.\n", id);
+	online_db->foreach(online_db, login_online_db_setoffline, id);
+	RFIFOSKIP(fd,2);
+	return 1;
+}
+
+/**
+ * Request to change PIN Code for an account.
+ * @param fd: fd to parse from (char-serv)
+ * @return 0 fail (packet does not have enough data), 1 success
+ */
+int logchrif_parse_updpincode(int fd){
+	if( RFIFOREST(fd) < 11 )
+		return 0;
+	else{
+		struct mmo_account acc;
+		AccountDB* accounts = login_get_accounts_db();
+
+		if( accounts->load_num(accounts, &acc, RFIFOL(fd,2) ) ){
+			strncpy( acc.pincode, (char*)RFIFOP(fd,6), 5 );
+			acc.pincode_change = time( NULL );
+			accounts->save(accounts, &acc);
+		}
+		RFIFOSKIP(fd,11);
+	}
+	return 1;
+}
+
+/**
+ * PIN Code was incorrectly entered too many times.
+ * @param fd: fd to parse from (char-serv)
+ * @return 0 fail (packet does not have enough data), 1 success (continue parsing)
+ */
+int logchrif_parse_pincode_authfail(int fd){
+	if( RFIFOREST(fd) < 6 )
+		return 0;
+	else{
+		struct mmo_account acc;
+		AccountDB* accounts = login_get_accounts_db();
+		if( accounts->load_num(accounts, &acc, RFIFOL(fd,2) ) ){
+			struct online_login_data* ld;
+
+			ld = (struct online_login_data*)idb_get(online_db,acc.account_id);
+
+			if( ld == NULL )
+				return 0;
+
+			login_log( host2ip(acc.last_ip), acc.userid, 100, "PIN Code check failed" );
+		}
+		login_remove_online_user(acc.account_id);
+		RFIFOSKIP(fd,6);
+	}
+	return 1;
+}
+
+/**
+ * Request the bank info of login
+ * @param fd: fd to parse from (char-serv)
+ * @param id: char serv id
+ * @param ip: char-serv ip (used for info)
+ * @return 0 fail (packet does not have enough data), 1 success (continue parsing)
+ */
+int logchrif_parse_bankvault(int fd, int id, char* ip){
+	if( RFIFOREST(fd) < 11 )
+		return 0;
+	else {
+		struct mmo_account acc;
+
+
+		int account_id = RFIFOL(fd,2);
+		char type = RFIFOB(fd,6);
+		int32 data = RFIFOL(fd,7);
+		RFIFOSKIP(fd,11);
+
+		AccountDB* accounts = login_get_accounts_db();
+		if( !accounts->load_num(accounts, &acc, account_id) )
+			ShowNotice("Char-server '%s': Error on banking  (account: %d not found, ip: %s).\n", ch_server[id].name, account_id, ip);
+		else{
+			unsigned char buf[12];
+			if(type==2){ // upd and Save
+				acc.bank_vault = data;
+				accounts->save(accounts, &acc);
+				WBUFB(buf,10) = 1;
+			} else {
+				WBUFB(buf,10) = 0;
+			}
+			// announce to other servers
+			WBUFW(buf,0) = 0x2741;
+			WBUFL(buf,2) = account_id;
+			WBUFL(buf,6) = acc.bank_vault;
+			logchrif_sendallwos(-1, buf, 11);
+		}
+	}
+	return 1;
+}
+
+/**
+ * Received a vip data reqest from char
+ * type is the query to perform
+ *  &1 : Select info and update old_groupid
+ *  &2 : Update vip time
+ * @param fd link to charserv
+ * @return 0 missing data, 1 succeeded
+ */
+int logchrif_parse_reqvipdata(int fd) {
+#ifdef VIP_ENABLE
+	if( RFIFOREST(fd) < 15 )
+		return 0;
+	else { //request vip info
+		struct mmo_account acc;
+		int aid = RFIFOL(fd,2);
+		int8 type = RFIFOB(fd,6);
+		int32 timediff = RFIFOL(fd,7);
+		int mapfd = RFIFOL(fd,11);
+		RFIFOSKIP(fd,15);
+		
+		if( accounts->load_num(accounts, &acc, aid ) ) {
+			time_t now = time(NULL);
+			time_t vip_time = acc.vip_time;
+			bool isvip = false;
+
+			if( acc.group_id > login_config.vip_sys.group ) { //Don't change group if it's higher.
+				chrif_sendvipdata(fd,acc,false,true,mapfd);
+				return 1;
+			}
+			if( type&2 ) {
+				if(!vip_time) vip_time = now; //new entry
+				vip_time += timediff; // set new duration
+			}
+			if( now < vip_time ) { //isvip
+				if(acc.group_id != login_config.vip_sys.group) //only upd this if we're not vip already
+					acc.old_group = acc.group_id;
+				acc.group_id = login_config.vip_sys.group;
+				acc.char_slots = login_config.char_per_account + login_config.vip_sys.char_increase;
+				isvip = true;
+			} else { //expired or @vip -xx
+				vip_time = 0;
+				if(acc.group_id == login_config.vip_sys.group) //prevent alteration in case account wasn't registered as vip yet
+					acc.group_id = acc.old_group;
+				acc.old_group = 0;
+				acc.char_slots = login_config.char_per_account;
+			}
+			acc.vip_time = vip_time;
+			accounts->save(accounts,&acc);
+			if( type&1 ) chrif_sendvipdata(fd,acc,isvip,false,mapfd);
+		}
+	}
+#endif
+	return 1;
+}
+
+/**
+ * Entry point from char-server to log-server.
+ * Function that checks incoming command, then splits it to the correct handler.
+ * @param fd: file descriptor to parse, (link to char-serv)
+ * @return 0=invalid server,marked for disconnection,unknow packet; 1=success
+ */
+int logchrif_parse(int fd){
+	int cid; //char-serv id
+	uint32 ipl;
+	char ip[16];
+
+	ARR_FIND( 0, ARRAYLENGTH(ch_server), cid, ch_server[cid].fd == fd );
+	if( cid == ARRAYLENGTH(ch_server) ){// not a char server
+		ShowDebug("logchrif_parse: Disconnecting invalid session #%d (is not a char-server)\n", fd);
+		set_eof(fd);
+		do_close(fd);
+		return 0;
+	}
+
+	if( session[fd]->flag.eof ){
+		do_close(fd);
+		ch_server[cid].fd = -1;
+		logchrif_on_disconnect(cid);
+		return 0;
+	}
+
+	ipl = ch_server[cid].ip;
+	ip2str(ipl, ip);
+
+	while( RFIFOREST(fd) >= 2 ){
+		int next = 1;
+		uint16 command = RFIFOW(fd,0);
+		switch( command ){
+		case 0x2712: next = logchrif_parse_reqauth(fd, cid, ip); break;
+		case 0x2714: next = logchrif_parse_ackusercount(fd, cid); break;
+		case 0x2715: next = logchrif_parse_updmail(fd, cid, ip); break;
+		case 0x2716: next = logchrif_parse_reqaccdata(fd, cid, ip); break;
+		case 0x2719: next = logchrif_parse_keepalive(fd); break;
+		case 0x2722: next = logchrif_parse_reqchangemail(fd,cid,ip); break;
+		case 0x2724: next = logchrif_parse_requpdaccstate(fd,cid,ip); break;
+		case 0x2725: next = logchrif_parse_reqbanacc(fd,cid,ip); break;
+		case 0x2727: next = logchrif_parse_reqchgsex(fd,cid,ip); break;
+		case 0x2728: next = logchrif_parse_updreg2(fd,cid,ip); break;
+		case 0x272a: next = logchrif_parse_requnbanacc(fd,cid,ip); break;
+		case 0x272b: next = logchrif_parse_setacconline(fd,cid); break;
+		case 0x272c: next = logchrif_parse_setaccoffline(fd); break;
+		case 0x272d: next = logchrif_parse_updonlinedb(fd,cid); break;
+		case 0x272e: next = logchrif_parse_reqacc2reg(fd); break;
+		case 0x2736: next = logchrif_parse_updcharip(fd,cid); break;
+		case 0x2737: next = logchrif_parse_setalloffline(fd,cid); break;
+		case 0x2738: next = logchrif_parse_updpincode(fd); break;
+		case 0x2739: next = logchrif_parse_pincode_authfail(fd); break;
+		case 0x2740: next = logchrif_parse_bankvault(fd,cid,ip); break;
+		case 0x2742: next = logchrif_parse_reqvipdata(fd); break; //Vip sys
+		default:
+			ShowError("logchrif_parse: Unknown packet 0x%x from a char-server! Disconnecting!\n", command);
+			set_eof(fd);
+			return 0;
+		} // switch
+		if(next==0) return 0; // avoid processing of followup packets (prev was probably incomplete)
+	} // while
+	return 1; //or 0
+}
+
+
+
+
+/// Constructor destructor and signal handlers
+
+/**
+ * Initializes a server structure.
+ * @param id: id of char-serv (should be >0, FIXME)
+ */
+void logchrif_server_init(int id) {
+	memset(&ch_server[id], 0, sizeof(ch_server[id]));
+	ch_server[id].fd = -1;
+}
+
+/**
+ * Destroys a server structure.
+ * @param id: id of char-serv (should be >0, FIXME)
+ */
+void logchrif_server_destroy(int id){
+	if( ch_server[id].fd != -1 ) {
+		do_close(ch_server[id].fd);
+		ch_server[id].fd = -1;
+	}
+}
+
+/**
+ * Resets all the data related to a server.
+ *  Actually destroys then recreates the struct.
+ * @param id: id of char-serv (should be >0, FIXME)
+ */
+void logchrif_server_reset(int id) {
+	online_db->foreach(online_db, login_online_db_setoffline, id); //Set all chars from this char server to offline.
+	logchrif_server_destroy(id);
+	logchrif_server_init(id);
+}
+
+/**
+ * Called when the connection to Char Server is disconnected.
+ * @param id: id of char-serv (should be >0, FIXME)
+ */
+void logchrif_on_disconnect(int id) {
+	ShowStatus("Char-server '%s' has disconnected.\n", ch_server[id].name);
+	logchrif_server_reset(id);
+}
+
+/**
+ * loginchrif constructor
+ *  Initialisation, function called at start of the login-serv.
+ */
+void do_init_loginchrif(void){
+	int i;
+	for( i = 0; i < ARRAYLENGTH(ch_server); ++i )
+		logchrif_server_init(i);
+
+	// add timer to detect ip address change and perform update
+	if (login_config.ip_sync_interval) {
+		add_timer_func_list(logchrif_sync_ip_addresses, "sync_ip_addresses");
+		add_timer_interval(gettick() + login_config.ip_sync_interval, logchrif_sync_ip_addresses, 0, 0, login_config.ip_sync_interval);
+	}
+}
+
+/**
+ * Signal handler
+ *  This function attempts to properly close the server when an interrupt signal is received.
+ *  current signal catch : SIGTERM, SIGINT
+ */
+void do_shutdown_loginchrif(void){
+	int id;
+	for( id = 0; id < ARRAYLENGTH(ch_server); ++id )
+		logchrif_server_reset(id);
+}
+
+/**
+ * loginchrif destructor
+ *  dealloc..., function called at exit of the login-serv
+ */
+void do_final_loginchrif(void){
+	int i;
+	for( i = 0; i < ARRAYLENGTH(ch_server); ++i )
+		logchrif_server_destroy(i);
+}

+ 56 - 0
src/login/loginchrif.h

@@ -0,0 +1,56 @@
+/**
+ * @file loginchrif.h
+ * Module purpose is to handle incoming and outgoing requests with char-server.
+ * Licensed under GNU GPL.
+ *  For more information, see LICENCE in the main folder.
+ * @author Athena Dev Teams originally in login.c
+ * @author rAthena Dev Team
+ */
+
+#ifndef LOGINCHRIF_H
+#define	LOGINCHRIF_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/**
+ * Entry point from char-server to log-server.
+ * Function that checks incoming command, then splits it to the correct handler.
+ * @param fd: file descriptor to parse, (link to char-serv)
+ * @return 0=invalid server,marked for disconnection,unknow packet; 1=success
+ */
+int logchrif_parse(int fd);
+
+/**
+ * Packet send to all char-servers, except one. (wos: without our self)
+ * @param sfd: fd to discard sending to
+ * @param buf: packet to send in form of an array buffer
+ * @param len: size of packet
+ * @return : the number of char-serv the packet was sent to
+ */
+int logchrif_sendallwos(int sfd, uint8* buf, size_t len);
+
+/**
+ * loginchrif constructor
+ *  Initialisation, function called at start of the login-serv.
+ */
+void do_init_loginchrif(void);
+/**
+ * Signal handler
+ *  This function attempts to properly close the server when an interrupt signal is received.
+ *  current signal catch : SIGTERM, SIGINT
+ */
+void do_shutdown_loginchrif(void);
+/**
+ * loginchrif destructor
+ *  dealloc..., function called at exit of the login-serv
+ */
+void do_final_loginchrif(void);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* LOGINCHRIF_H */
+

+ 564 - 0
src/login/loginclif.c

@@ -0,0 +1,564 @@
+/**
+ * @file loginclif.c
+ * Module purpose is to handle incoming and outgoing requests with client.
+ * Licensed under GNU GPL.
+ *  For more information, see LICENCE in the main folder.
+ * @author Athena Dev Teams originally in login.c
+ * @author rAthena Dev Team
+ */
+
+#include "../common/timer.h" //difftick
+#include "../common/strlib.h" //safeprint
+#include "../common/showmsg.h" //show notice
+#include "../common/socket.h" //wfifo session
+#include "../common/malloc.h"
+#include "../common/utils.h"
+#include "../common/md5calc.h"
+#include "../common/random.h"
+#include "account.h"
+#include "ipban.h" //ipban_check
+#include "login.h"
+#include "loginlog.h"
+#include "loginclif.h"
+#include "loginchrif.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/**
+ * Transmit auth result to client.
+ * @param fd: client file desciptor link
+ * @param result: result to transmit to client, see below
+ *  1 : Server closed
+ *  2 : Someone has already logged in with this id
+ *  8 : already online
+ * <result>.B (SC_NOTIFY_BAN)
+ */
+static void logclif_sent_auth_result(int fd,char result){
+	WFIFOHEAD(fd,3);
+	WFIFOW(fd,0) = 0x81;
+	WFIFOB(fd,2) = result;
+	WFIFOSET(fd,3);
+}
+
+/**
+ * Auth successful, inform client and create a temp auth_node.
+ * @param sd: player session
+ */
+static void logclif_auth_ok(struct login_session_data* sd) {
+	int fd = sd->fd;
+	uint32 ip = session[fd]->client_addr;
+
+	uint8 server_num, n;
+	uint32 subnet_char_ip;
+	struct auth_node* node;
+	int i;
+
+	if( runflag != LOGINSERVER_ST_RUNNING ){
+		// players can only login while running
+		logclif_sent_auth_result(fd,1); // server closed
+		return;
+	}
+
+	if( login_config.group_id_to_connect >= 0 && sd->group_id != login_config.group_id_to_connect ) {
+		ShowStatus("Connection refused: the required group id for connection is %d (account: %s, group: %d).\n", login_config.group_id_to_connect, sd->userid, sd->group_id);
+		logclif_sent_auth_result(fd,1); // server closed
+		return;
+	} else if( login_config.min_group_id_to_connect >= 0 && login_config.group_id_to_connect == -1 && sd->group_id < login_config.min_group_id_to_connect ) {
+		ShowStatus("Connection refused: the minium group id required for connection is %d (account: %s, group: %d).\n", login_config.min_group_id_to_connect, sd->userid, sd->group_id);
+		logclif_sent_auth_result(fd,1); // server closed
+		return;
+	}
+
+	server_num = 0;
+	for( i = 0; i < ARRAYLENGTH(ch_server); ++i )
+		if( session_isActive(ch_server[i].fd) )
+			server_num++;
+
+	if( server_num == 0 )
+	{// if no char-server, don't send void list of servers, just disconnect the player with proper message
+		ShowStatus("Connection refused: there is no char-server online (account: %s).\n", sd->userid);
+		logclif_sent_auth_result(fd,1); // server closed
+		return;
+	}
+
+	{
+		struct online_login_data* data = (struct online_login_data*)idb_get(online_db, sd->account_id);
+		if( data )
+		{// account is already marked as online!
+			if( data->char_server > -1 )
+			{// Request char servers to kick this account out. [Skotlex]
+				uint8 buf[6];
+				ShowNotice("User '%s' is already online - Rejected.\n", sd->userid);
+				WBUFW(buf,0) = 0x2734;
+				WBUFL(buf,2) = sd->account_id;
+				logchrif_sendallwos(-1, buf, 6);
+				if( data->waiting_disconnect == INVALID_TIMER )
+					data->waiting_disconnect = add_timer(gettick()+AUTH_TIMEOUT, login_waiting_disconnect_timer, sd->account_id, 0);
+				logclif_sent_auth_result(fd,8); // 08 = Server still recognizes your last login
+				return;
+			}
+			else
+			if( data->char_server == -1 )
+			{// client has authed but did not access char-server yet
+				// wipe previous session
+				idb_remove(auth_db, sd->account_id);
+				login_remove_online_user(sd->account_id);
+				data = NULL;
+			}
+		}
+	}
+
+	login_log(ip, sd->userid, 100, "login ok");
+	ShowStatus("Connection of the account '%s' accepted.\n", sd->userid);
+
+	WFIFOHEAD(fd,47+32*server_num);
+	WFIFOW(fd,0) = 0x69;
+	WFIFOW(fd,2) = 47+32*server_num;
+	WFIFOL(fd,4) = sd->login_id1;
+	WFIFOL(fd,8) = sd->account_id;
+	WFIFOL(fd,12) = sd->login_id2;
+	WFIFOL(fd,16) = 0; // in old version, that was for ip (not more used)
+	//memcpy(WFIFOP(fd,20), sd->lastlogin, 24); // in old version, that was for name (not more used)
+	memset(WFIFOP(fd,20), 0, 24);
+	WFIFOW(fd,44) = 0; // unknown
+	WFIFOB(fd,46) = sex_str2num(sd->sex);
+	for( i = 0, n = 0; i < ARRAYLENGTH(ch_server); ++i ) {
+		if( !session_isValid(ch_server[i].fd) )
+			continue;
+		subnet_char_ip = lan_subnetcheck(ip); // Advanced subnet check [LuzZza]
+		WFIFOL(fd,47+n*32) = htonl((subnet_char_ip) ? subnet_char_ip : ch_server[i].ip);
+		WFIFOW(fd,47+n*32+4) = ntows(htons(ch_server[i].port)); // [!] LE byte order here [!]
+		memcpy(WFIFOP(fd,47+n*32+6), ch_server[i].name, 20);
+		WFIFOW(fd,47+n*32+26) = ch_server[i].users;
+		WFIFOW(fd,47+n*32+28) = ch_server[i].type;
+		WFIFOW(fd,47+n*32+30) = ch_server[i].new_;
+		n++;
+	}
+	WFIFOSET(fd,47+32*server_num);
+
+	// create temporary auth entry
+	CREATE(node, struct auth_node, 1);
+	node->account_id = sd->account_id;
+	node->login_id1 = sd->login_id1;
+	node->login_id2 = sd->login_id2;
+	node->sex = sd->sex;
+	node->ip = ip;
+	node->version = sd->version;
+	node->clienttype = sd->clienttype;
+	idb_put(auth_db, sd->account_id, node);
+	{
+		struct online_login_data* data;
+		// mark client as 'online'
+		data = login_add_online_user(-1, sd->account_id);
+		// schedule deletion of this node
+		data->waiting_disconnect = add_timer(gettick()+AUTH_TIMEOUT, login_waiting_disconnect_timer, sd->account_id, 0);
+	}
+}
+
+/**
+ * Inform client that auth has failed.
+ * @param sd: player session
+ * @param result: nb (msg define in conf)
+    0 = Unregistered ID
+    1 = Incorrect Password
+    2 = This ID is expired
+    3 = Rejected from Server
+    4 = You have been blocked by the GM Team
+    5 = Your Game's EXE file is not the latest version
+    6 = Your are Prohibited to log in until %s
+    7 = Server is jammed due to over populated
+    8 = No more accounts may be connected from this company
+    9 = MSI_REFUSE_BAN_BY_DBA
+    10 = MSI_REFUSE_EMAIL_NOT_CONFIRMED
+    11 = MSI_REFUSE_BAN_BY_GM
+    12 = MSI_REFUSE_TEMP_BAN_FOR_DBWORK
+    13 = MSI_REFUSE_SELF_LOCK
+    14 = MSI_REFUSE_NOT_PERMITTED_GROUP
+    15 = MSI_REFUSE_NOT_PERMITTED_GROUP
+    99 = This ID has been totally erased
+    100 = Login information remains at %s
+    101 = Account has been locked for a hacking investigation. Please contact the GM Team for more information
+    102 = This account has been temporarily prohibited from login due to a bug-related investigation
+    103 = This character is being deleted. Login is temporarily unavailable for the time being
+    104 = This character is being deleted. Login is temporarily unavailable for the time being
+     default = Unknown Error.
+ */
+static void logclif_auth_failed(struct login_session_data* sd, int result) {
+	int fd = sd->fd;
+	uint32 ip = session[fd]->client_addr;
+
+	if (login_config.log_login)
+	{
+		if(result >= 0 && result <= 15)
+		    login_log(ip, sd->userid, result, msg_txt(result));
+		else if(result >= 99 && result <= 104)
+		    login_log(ip, sd->userid, result, msg_txt(result-83)); //-83 offset
+		else
+		    login_log(ip, sd->userid, result, msg_txt(22)); //unknow error
+	}
+
+	if( result == 1 && login_config.dynamic_pass_failure_ban )
+		ipban_log(ip); // log failed password attempt
+
+//#if PACKETVER >= 20120000 /* not sure when this started */
+	if( sd->version >= date2version(20120000) ){ /* not sure when this started */
+		WFIFOHEAD(fd,26);
+		WFIFOW(fd,0) = 0x83e;
+		WFIFOL(fd,2) = result;
+		if( result != 6 )
+			memset(WFIFOP(fd,6), '\0', 20);
+		else { // 6 = Your are Prohibited to log in until %s
+			struct mmo_account acc;
+			AccountDB* accounts = login_get_accounts_db();
+			time_t unban_time = ( accounts->load_str(accounts, &acc, sd->userid) ) ? acc.unban_time : 0;
+			timestamp2string((char*)WFIFOP(fd,6), 20, unban_time, login_config.date_format);
+		}
+		WFIFOSET(fd,26);
+	}
+//#else	
+	else {
+		WFIFOHEAD(fd,23);
+		WFIFOW(fd,0) = 0x6a;
+		WFIFOB(fd,2) = (uint8)result;
+		if( result != 6 )
+			memset(WFIFOP(fd,3), '\0', 20);
+		else { // 6 = Your are Prohibited to log in until %s
+			struct mmo_account acc;
+			AccountDB* accounts = login_get_accounts_db();
+			time_t unban_time = ( accounts->load_str(accounts, &acc, sd->userid) ) ? acc.unban_time : 0;
+			timestamp2string((char*)WFIFOP(fd,3), 20, unban_time, login_config.date_format);
+		}
+		WFIFOSET(fd,23);
+	}
+//#endif	
+}
+
+/**
+ * Received a keepalive packet to maintain connection.
+ * 0x200 <account.userid>.24B.
+ * @param fd: fd to parse from (client fd)
+ * @return 0 not enough info transmitted, 1 success
+ */
+static int logclif_parse_keepalive(int fd){
+	if (RFIFOREST(fd) < 26)
+		return 0;
+	RFIFOSKIP(fd,26);
+	return 1;
+}
+
+/**
+ * Received a keepalive packet to maintain connection.
+ * S 0204 <md5 hash>.16B (kRO 2004-05-31aSakexe langtype 0 and 6)
+ * @param fd: fd to parse from (client fd)
+ * @return 0 not enough info transmitted, 1 success
+ */
+static int logclif_parse_updclhash(int fd, struct login_session_data *sd){
+	if (RFIFOREST(fd) < 18)
+		return 0;
+	sd->has_client_hash = 1;
+	memcpy(sd->client_hash, RFIFOP(fd, 2), 16);
+	RFIFOSKIP(fd,18);
+	return 1;
+}
+
+/**
+ * Received a connection request.
+ * @param fd: file descriptor to parse from (client)
+ * @param sd: client session
+ * @param command: packet type sent
+ * @param ip: ipv4 address (client)
+ *  S 0064 <version>.L <username>.24B <password>.24B <clienttype>.B
+ *  S 0277 <version>.L <username>.24B <password>.24B <clienttype>.B <ip address>.16B <adapter address>.13B
+ *  S 02b0 <version>.L <username>.24B <password>.24B <clienttype>.B <ip address>.16B <adapter address>.13B <g_isGravityID>.B
+ *  S 01dd <version>.L <username>.24B <password hash>.16B <clienttype>.B
+ *  S 01fa <version>.L <username>.24B <password hash>.16B <clienttype>.B <?>.B(index of the connection in the clientinfo file (+10 if the command-line contains "pc"))
+ *  S 027c <version>.L <username>.24B <password hash>.16B <clienttype>.B <?>.13B(junk)
+ *  S 0825 <packetsize>.W <version>.L <clienttype>.B <userid>.24B <password>.27B <mac>.17B <ip>.15B <token>.(packetsize - 0x5C)B
+ * @param fd: fd to parse from (client fd)
+ * @return 0 failure, 1 success
+ */
+static int logclif_parse_reqauth(int fd, struct login_session_data *sd, int command, char* ip){
+	size_t packet_len = RFIFOREST(fd);
+
+	if( (command == 0x0064 && packet_len < 55)
+	||  (command == 0x0277 && packet_len < 84)
+	||  (command == 0x02b0 && packet_len < 85)
+	||  (command == 0x01dd && packet_len < 47)
+	||  (command == 0x01fa && packet_len < 48)
+	||  (command == 0x027c && packet_len < 60)
+	||  (command == 0x0825 && (packet_len < 4 || packet_len < RFIFOW(fd, 2))) )
+		return 0;
+	else {
+		int result;
+		uint32 version;
+		char username[NAME_LENGTH];
+		char password[PASSWD_LENGTH];
+		unsigned char passhash[16];
+		uint8 clienttype;
+		bool israwpass = (command==0x0064 || command==0x0277 || command==0x02b0 || command == 0x0825);
+
+		// Shinryo: For the time being, just use token as password.
+		if(command == 0x0825) {
+			char *accname = (char *)RFIFOP(fd, 9);
+			char *token = (char *)RFIFOP(fd, 0x5C);
+			size_t uAccLen = strlen(accname);
+			size_t uTokenLen = RFIFOREST(fd) - 0x5C;
+
+			version = RFIFOL(fd,4);
+
+			if(uAccLen > NAME_LENGTH - 1 || uAccLen <= 0 || uTokenLen > NAME_LENGTH - 1  || uTokenLen <= 0)
+			{
+				logclif_auth_failed(sd, 3);
+				return 0;
+			}
+
+			safestrncpy(username, accname, uAccLen + 1);
+			safestrncpy(password, token, uTokenLen + 1);
+			clienttype = RFIFOB(fd, 8);
+		}
+		else
+		{
+			version = RFIFOL(fd,2);
+			safestrncpy(username, (const char*)RFIFOP(fd,6), NAME_LENGTH);
+			if( israwpass )
+			{
+				safestrncpy(password, (const char*)RFIFOP(fd,30), PASSWD_LENGTH);
+				clienttype = RFIFOB(fd,54);
+			}
+			else
+			{
+				memcpy(passhash, RFIFOP(fd,30), 16);
+				clienttype = RFIFOB(fd,46);
+			}
+		}
+		RFIFOSKIP(fd,RFIFOREST(fd)); // assume no other packet was sent
+
+		sd->clienttype = clienttype;
+		sd->version = version;
+		safestrncpy(sd->userid, username, NAME_LENGTH);
+		if( israwpass )
+		{
+			ShowStatus("Request for connection of %s (ip: %s) version=%d\n", sd->userid, ip,sd->version);
+			safestrncpy(sd->passwd, password, NAME_LENGTH);
+			if( login_config.use_md5_passwds )
+				MD5_String(sd->passwd, sd->passwd);
+			sd->passwdenc = 0;
+		}
+		else
+		{
+			ShowStatus("Request for connection (passwdenc mode) of %s (ip: %s) version=%d\n", sd->userid, ip,sd->version);
+			bin2hex(sd->passwd, passhash, 16); // raw binary data here!
+			sd->passwdenc = PASSWORDENC;
+		}
+
+		if( sd->passwdenc != 0 && login_config.use_md5_passwds )
+		{
+			logclif_auth_failed(sd, 3); // send "rejected from server"
+			return 0;
+		}
+
+		result = login_mmo_auth(sd, false);
+
+		if( result == -1 )
+			logclif_auth_ok(sd);
+		else
+			logclif_auth_failed(sd, result);
+	}
+	return 1;
+}
+
+/**
+ * Client requests an md5key for his session: keys will be generated and sent back.
+ * @param fd: file descriptor to parse from (client)
+ * @param sd: client session
+ * @return 1 success
+ */
+static int logclif_parse_reqkey(int fd, struct login_session_data *sd){
+	RFIFOSKIP(fd,2);
+	{
+		memset(sd->md5key, '\0', sizeof(sd->md5key));
+		sd->md5keylen = (uint16)(12 + rnd() % 4);
+		MD5_Salt(sd->md5keylen, sd->md5key);
+
+		WFIFOHEAD(fd,4 + sd->md5keylen);
+		WFIFOW(fd,0) = 0x01dc;
+		WFIFOW(fd,2) = 4 + sd->md5keylen;
+		memcpy(WFIFOP(fd,4), sd->md5key, sd->md5keylen);
+		WFIFOSET(fd,WFIFOW(fd,2));
+	}
+	return 1;
+}
+
+/**
+ * Char-server request to connect to the login-server.
+ * This is needed to exchange packets.
+ * @param fd: file descriptor to parse from (client)
+ * @param sd: client session
+ * @param ip: ipv4 address (client)
+ * @return 0 packet received too shirt, 1 success
+ */
+static int logclif_parse_reqcharconnec(int fd, struct login_session_data *sd, char* ip){
+	if (RFIFOREST(fd) < 86)
+		return 0;
+	{
+		int result;
+		char server_name[20];
+		char message[256];
+		uint32 server_ip;
+		uint16 server_port;
+		uint16 type;
+		uint16 new_;
+
+		safestrncpy(sd->userid, (char*)RFIFOP(fd,2), NAME_LENGTH);
+		safestrncpy(sd->passwd, (char*)RFIFOP(fd,26), NAME_LENGTH);
+		if( login_config.use_md5_passwds )
+			MD5_String(sd->passwd, sd->passwd);
+		sd->passwdenc = 0;
+		sd->version = login_config.client_version_to_connect; // hack to skip version check
+		server_ip = ntohl(RFIFOL(fd,54));
+		server_port = ntohs(RFIFOW(fd,58));
+		safestrncpy(server_name, (char*)RFIFOP(fd,60), 20);
+		type = RFIFOW(fd,82);
+		new_ = RFIFOW(fd,84);
+		RFIFOSKIP(fd,86);
+
+		ShowInfo("Connection request of the char-server '%s' @ %u.%u.%u.%u:%u (account: '%s', pass: '%s', ip: '%s')\n", server_name, CONVIP(server_ip), server_port, sd->userid, sd->passwd, ip);
+		sprintf(message, "charserver - %s@%u.%u.%u.%u:%u", server_name, CONVIP(server_ip), server_port);
+		login_log(session[fd]->client_addr, sd->userid, 100, message);
+
+		result = login_mmo_auth(sd, true);
+		if( runflag == LOGINSERVER_ST_RUNNING &&
+			result == -1 &&
+			sd->sex == 'S' &&
+			sd->account_id >= 0 && sd->account_id < ARRAYLENGTH(ch_server) &&
+			!session_isValid(ch_server[sd->account_id].fd) )
+		{
+			ShowStatus("Connection of the char-server '%s' accepted.\n", server_name);
+			safestrncpy(ch_server[sd->account_id].name, server_name, sizeof(ch_server[sd->account_id].name));
+			ch_server[sd->account_id].fd = fd;
+			ch_server[sd->account_id].ip = server_ip;
+			ch_server[sd->account_id].port = server_port;
+			ch_server[sd->account_id].users = 0;
+			ch_server[sd->account_id].type = type;
+			ch_server[sd->account_id].new_ = new_;
+
+			session[fd]->func_parse = logchrif_parse;
+			session[fd]->flag.server = 1;
+			realloc_fifo(fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK);
+
+			// send connection success
+			WFIFOHEAD(fd,3);
+			WFIFOW(fd,0) = 0x2711;
+			WFIFOB(fd,2) = 0;
+			WFIFOSET(fd,3);
+		}
+		else
+		{
+			ShowNotice("Connection of the char-server '%s' REFUSED.\n", server_name);
+			WFIFOHEAD(fd,3);
+			WFIFOW(fd,0) = 0x2711;
+			WFIFOB(fd,2) = 3;
+			WFIFOSET(fd,3);
+		}
+	}
+	return 1;
+}
+
+/**
+ * Entry point from client to log-server.
+ * Function that checks incoming command, then splits it to the correct handler.
+ * @param fd: file descriptor to parse, (link to client)
+ * @return 0=invalid session,marked for disconnection,unknow packet, banned..; 1=success
+ */
+int logclif_parse(int fd) {
+	struct login_session_data* sd = (struct login_session_data*)session[fd]->session_data;
+
+	char ip[16];
+	uint32 ipl = session[fd]->client_addr;
+	ip2str(ipl, ip);
+
+	if( session[fd]->flag.eof )
+	{
+		ShowInfo("Closed connection from '"CL_WHITE"%s"CL_RESET"'.\n", ip);
+		do_close(fd);
+		return 0;
+	}
+
+	if( sd == NULL )
+	{
+		// Perform ip-ban check
+		if( login_config.ipban && ipban_check(ipl) )
+		{
+			ShowStatus("Connection refused: IP isn't authorised (deny/allow, ip: %s).\n", ip);
+			login_log(ipl, "unknown", -3, "ip banned");
+			WFIFOHEAD(fd,23);
+			WFIFOW(fd,0) = 0x6a;
+			WFIFOB(fd,2) = 3; // 3 = Rejected from Server
+			WFIFOSET(fd,23);
+			set_eof(fd);
+			return 0;
+		}
+		// create a session for this new connection
+		CREATE(session[fd]->session_data, struct login_session_data, 1);
+		sd = (struct login_session_data*)session[fd]->session_data;
+		sd->fd = fd;
+	}
+
+	while( RFIFOREST(fd) >= 2 )
+	{
+		uint16 command = RFIFOW(fd,0);
+		int next=1;
+
+		switch( command )
+		{
+		// New alive packet: used to verify if client is always alive.
+		case 0x0200: next = logclif_parse_keepalive(fd); break;
+		// client md5 hash (binary)
+		case 0x0204: next = logclif_parse_updclhash(fd,sd); break;
+		// request client login (raw password)
+		case 0x0064: // S 0064 <version>.L <username>.24B <password>.24B <clienttype>.B
+		case 0x0277: // S 0277 <version>.L <username>.24B <password>.24B <clienttype>.B <ip address>.16B <adapter address>.13B
+		case 0x02b0: // S 02b0 <version>.L <username>.24B <password>.24B <clienttype>.B <ip address>.16B <adapter address>.13B <g_isGravityID>.B
+		// request client login (md5-hashed password)
+		case 0x01dd: // S 01dd <version>.L <username>.24B <password hash>.16B <clienttype>.B
+		case 0x01fa: // S 01fa <version>.L <username>.24B <password hash>.16B <clienttype>.B <?>.B(index of the connection in the clientinfo file (+10 if the command-line contains "pc"))
+		case 0x027c: // S 027c <version>.L <username>.24B <password hash>.16B <clienttype>.B <?>.13B(junk)
+		case 0x0825: // S 0825 <packetsize>.W <version>.L <clienttype>.B <userid>.24B <password>.27B <mac>.17B <ip>.15B <token>.(packetsize - 0x5C)B
+			next = logclif_parse_reqauth(fd,  sd, command, ip); 
+			break;
+		// Sending request of the coding key
+		case 0x01db: next = logclif_parse_reqkey(fd, sd); break;
+		// Connection request of a char-server
+		case 0x2710: logclif_parse_reqcharconnec(fd,sd, ip); return 0; // processing will continue elsewhere
+		default:
+			ShowNotice("Abnormal end of connection (ip: %s): Unknown packet 0x%x\n", ip, command);
+			set_eof(fd);
+			return 0;
+		}
+		if(next==0) return 0; // avoid processing of followup packets (prev was probably incomplete)
+	}
+
+	return 0;
+}
+
+
+
+/// Constructor destructor
+
+/**
+ * Initialize the module.
+ * Launched at login-serv start, create db or other long scope variable here.
+ */
+void do_init_loginclif(void){
+	return;
+}
+
+/**
+ * loginclif destructor
+ *  dealloc..., function called at exit of the login-serv
+ */
+void do_final_loginclif(void){
+	return;
+}

+ 42 - 0
src/login/loginclif.h

@@ -0,0 +1,42 @@
+/**
+ * @file loginclif.h
+ * Module purpose is to handle incoming and outgoing requests with client.
+ * Licensed under GNU GPL.
+ *  For more information, see LICENCE in the main folder.
+ * @author Athena Dev Teams originally in login.c
+ * @author rAthena Dev Team
+ */
+
+#ifndef _LOGINCLIF_H
+#define	_LOGINCLIF_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/**
+ * Entry point from client to log-server.
+ * Function that checks incoming command, then splits it to the correct handler.
+ * @param fd: file descriptor to parse, (link to client)
+ * @return 0=invalid session,marked for disconnection,unknow packet, banned..; 1=success
+ */
+int logclif_parse(int fd);
+
+/**
+ * Initialize the module.
+ * Launched at login-serv start, create db or other long scope variable here.
+ */
+void do_init_loginclif(void);
+
+/**
+ * loginclif destructor
+ *  dealloc..., function called at exit of the login-serv
+ */
+void do_final_loginclif(void);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _LOGINCLIF_H */
+

+ 176 - 0
src/login/logincnslif.c

@@ -0,0 +1,176 @@
+/**
+ * @file logincnslif.c
+ * Module purpose is to handle incoming and outgoing requests with console.
+ * Licensed under GNU GPL.
+ *  For more information, see LICENCE in the main folder.
+ * @author Athena Dev Teams originally in login.c
+ * @author rAthena Dev Team
+ */
+
+#include "../common/mmo.h" //cbasetype + NAME_LENGTH
+#include "../common/showmsg.h" //show notice
+#include "../common/md5calc.h"
+#include "../common/ers.h"
+#include "../common/cli.h"
+#include "../common/timer.h"
+#include "../common/strlib.h"
+#include "login.h"
+#include "logincnslif.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/**
+ * Login-server console help: starting option info.
+ *  Do not rename function used as extern.
+ * @param do_exit: terminate program execution ?
+ */
+void display_helpscreen(bool do_exit) {
+	ShowInfo("Usage: %s [options]\n", SERVER_NAME);
+	ShowInfo("\n");
+	ShowInfo("Options:\n");
+	ShowInfo("  -?, -h [--help]\t\tDisplays this help screen.\n");
+	ShowInfo("  -v [--version]\t\tDisplays the server's version.\n");
+	ShowInfo("  --run-once\t\t\tCloses server after loading (testing).\n");
+	ShowInfo("  --login-config <file>\t\tAlternative login-server configuration.\n");
+	ShowInfo("  --lan-config <file>\t\tAlternative lag configuration.\n");
+	ShowInfo("  --msg-config <file>\t\tAlternative message configuration.\n");
+	if( do_exit )
+		exit(EXIT_SUCCESS);
+}
+
+/**
+ * Read the option specified in command line
+ *  and assign the confs used by the different server.
+ * @param argc:
+ * @param argv:
+ * @return true or Exit on failure.
+ */
+int logcnslif_get_options(int argc, char ** argv) {
+	int i = 0;
+	for (i = 1; i < argc; i++) {
+		const char* arg = argv[i];
+
+		if (arg[0] != '-' && (arg[0] != '/' || arg[1] == '-')) {// -, -- and /
+			ShowError("Unknown option '%s'.\n", argv[i]);
+			exit(EXIT_FAILURE);
+		} else if ((++arg)[0] == '-') {// long option
+			arg++;
+
+			if (strcmp(arg, "help") == 0) {
+				display_helpscreen(true);
+			} else if (strcmp(arg, "version") == 0) {
+				display_versionscreen(true);
+			} else if (strcmp(arg, "run-once") == 0){ // close the map-server as soon as its done.. for testing [Celest]
+				runflag = CORE_ST_STOP;
+			} else if (SERVER_TYPE & (ATHENA_SERVER_LOGIN)) { //login
+				if (strcmp(arg, "lan-config") == 0) {
+					if (opt_has_next_value(arg, i, argc)) safestrncpy(login_config.lanconf_name, argv[++i], sizeof(login_config.lanconf_name));
+				}
+				if (strcmp(arg, "login-config") == 0) {
+					if (opt_has_next_value(arg, i, argc)) safestrncpy(login_config.loginconf_name, argv[++i], sizeof(login_config.loginconf_name));
+				}
+				if (strcmp(arg, "msg-config") == 0) {
+					if (opt_has_next_value(arg, i, argc)) safestrncpy(login_config.msgconf_name, argv[++i], sizeof(login_config.msgconf_name));
+				} else {
+					ShowError("Unknown option '%s'.\n", argv[i]);
+					exit(EXIT_FAILURE);
+				}
+			}
+		} else switch (arg[0]) {// short option
+			case '?':
+			case 'h':
+				display_helpscreen(true);
+				break;
+			case 'v':
+				display_versionscreen(true);
+				break;
+			default:
+				ShowError("Unknown option '%s'.\n", argv[i]);
+				exit(EXIT_FAILURE);
+		}
+	}
+	return 1;
+}
+
+/**
+ * Console Command Parser
+ * Transmited from command cli.c
+ * note common name for all serv do not rename (extern in cli)
+ * @author [Wizputer]
+ * @param buf: buffer to parse, (from console)
+ * @return 1=success
+ */
+int cnslif_parse(const char* buf){
+	char type[64];
+	char command[64];
+	int n=0;
+
+	if( ( n = sscanf(buf, "%127[^:]:%255[^\n\r]", type, command) ) < 2 ){
+		if((n = sscanf(buf, "%63[^\n]", type))<1) return -1; //nothing to do no arg
+	}
+	if( n != 2 ){ //end string
+		ShowNotice("Type: '%s'\n",type);
+		command[0] = '\0';
+	}
+	else
+		ShowNotice("Type of command: '%s' || Command: '%s'\n",type,command);
+
+	if( n == 2 ){
+		if(strcmpi("server", type) == 0 ){
+			if( strcmpi("shutdown", command) == 0 || strcmpi("exit", command) == 0 || strcmpi("quit", command) == 0 ){
+				runflag = 0;
+			}
+			else if( strcmpi("alive", command) == 0 || strcmpi("status", command) == 0 )
+				ShowInfo(CL_CYAN"Console: "CL_BOLD"I'm Alive."CL_RESET"\n");
+		}
+		if( strcmpi("create",type) == 0 )
+		{
+			char username[NAME_LENGTH], password[NAME_LENGTH], md5password[32+1], sex; //23+1 plaintext 32+1 md5
+			bool md5 = 0;
+			if( sscanf(command, "%23s %23s %c", username, password, &sex) < 3 || strnlen(username, sizeof(username)) < 4 || strnlen(password, sizeof(password)) < 1 ){
+				ShowWarning("Console: Invalid parameters for '%s'. Usage: %s <username> <password> <sex:F/M>\n", type, type);
+				return 0;
+			}
+			if( login_config.use_md5_passwds ){
+				MD5_String(password,md5password);
+				md5 = 1;
+			}
+			if( login_mmo_auth_new(username,(md5?md5password:password), TOUPPER(sex), "0.0.0.0") != -1 ){
+				ShowError("Console: Account creation failed.\n");
+				return 0;
+			}
+			ShowStatus("Console: Account '%s' created successfully.\n", username);
+		}
+	}
+	else if( strcmpi("ers_report", type) == 0 ){
+		ers_report();
+	}
+	else if( strcmpi("help", type) == 0 ){
+		ShowInfo("Available commands:\n");
+		ShowInfo("\t server:shutdown => Stops the server.\n");
+		ShowInfo("\t server:alive => Checks if the server is running.\n");
+		ShowInfo("\t ers_report => Displays database usage.\n");
+		ShowInfo("\t create:<username> <password> <sex:M|F> => Creates a new account.\n");
+	}
+	return 1;
+}
+
+/**
+ * Initialize the module.
+ * Launched at login-serv start, create db or other long scope variable here.
+ */
+void do_init_logincnslif(void){
+	if( login_config.console ) {
+		add_timer_func_list(parse_console_timer, "parse_console_timer");
+		add_timer_interval(gettick()+1000, parse_console_timer, 0, 0, 1000); //start in 1s each 1sec
+	}
+}
+
+/**
+ * Handler to cleanup module, called when login-server stops.
+ */
+void do_final_logincnslif(void){
+	return;
+}

+ 58 - 0
src/login/logincnslif.h

@@ -0,0 +1,58 @@
+/**
+ * @file logincnslif.h
+ * Module purpose is to handle incoming and outgoing requests with console.
+ * Licensed under GNU GPL.
+ *  For more information, see LICENCE in the main folder.
+ * @author Athena Dev Teams originally in login.c
+ * @author rAthena Dev Team
+ */
+
+#ifndef CONSOLEIF_H
+#define	CONSOLEIF_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/**
+ * Console Command Parser
+ * Transmited from command cli.c
+ * note common name for all serv do not rename (extern in cli)
+ * @author [Wizputer]
+ * @param buf: buffer to parse, (from console)
+ * @return 1=success
+ */
+int cnslif_parse(const char* buf);
+
+/**
+ * Read the option specified in command line
+ *  and assign the confs used by the different server.
+ * @param argc:
+ * @param argv:
+ * @return true or Exit on failure.
+ */
+int logcnslif_get_options(int argc, char ** argv);
+
+/**
+ * Login-server console help: starting option info.
+ *  Do not rename function used as extern.
+ * @param do_exit: terminate program execution ?
+ */
+void display_helpscreen(bool do_exit);
+
+/**
+ * Initialize the module.
+ * Launched at login-serv start, create db or other long scope variable here.
+ */
+void do_init_logincnslif(void);
+/**
+ * Handler to cleanup module, called when login-server stops.
+ */
+void do_final_logincnslif(void);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* CONSOLEIF_H */
+

+ 95 - 65
src/login/loginlog_sql.c → src/login/loginlog.c

@@ -1,5 +1,11 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
+/**
+ * @file loginlog.c
+ * Module purpose is to register (log) events into a file or sql database.
+ * Licensed under GNU GPL.
+ *  For more information, see LICENCE in the main folder.
+ * @author Athena Dev Teams rev < 15k
+ * @author rAthena Dev Team
+ */
 
 #include "../common/cbasetypes.h"
 #include "../common/mmo.h"
@@ -13,7 +19,7 @@
 static char   global_db_hostname[32] = "127.0.0.1";
 static uint16 global_db_port = 3306;
 static char   global_db_username[32] = "ragnarok";
-static char   global_db_password[32] = "";
+static char   global_db_password[32] = ""; //empty by default since mysql is empty by default as well
 static char   global_db_database[32] = "ragnarok";
 static char   global_codepage[32] = "";
 // local sql settings
@@ -29,9 +35,13 @@ static Sql* sql_handle = NULL;
 static bool enabled = false;
 
 
-// Returns the number of failed login attemps by the ip in the last minutes.
-unsigned long loginlog_failedattempts(uint32 ip, unsigned int minutes)
-{
+/**
+ * Get the number of failed login attempts by the ip in the last minutes.
+ * @param ip: ip to search attempt from
+ * @param minutes: intervall to search
+ * @return number of failed attempts
+ */
+unsigned long loginlog_failedattempts(uint32 ip, unsigned int minutes) {
 	unsigned long failures = 0;
 
 	if( !enabled )
@@ -52,11 +62,14 @@ unsigned long loginlog_failedattempts(uint32 ip, unsigned int minutes)
 }
 
 
-/*=============================================
- * Records an event in the login log
- *---------------------------------------------*/
-void login_log(uint32 ip, const char* username, int rcode, const char* message)
-{
+/**
+ * Records an event in the login log.
+ * @param ip:
+ * @param username:
+ * @param rcode:
+ * @param message:
+ */
+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;
@@ -75,60 +88,13 @@ void login_log(uint32 ip, const char* username, int rcode, const char* message)
 		Sql_ShowDebug(sql_handle);
 }
 
-bool loginlog_init(void)
-{
-	const char* username;
-	const char* password;
-	const char* hostname;
-	uint16      port;
-	const char* database;
-	const char* codepage;
-
-	if( log_db_hostname[0] != '\0' )
-	{// local settings
-		username = log_db_username;
-		password = log_db_password;
-		hostname = log_db_hostname;
-		port     = log_db_port;
-		database = log_db_database;
-		codepage = log_codepage;
-	}
-	else
-	{// global settings
-		username = global_db_username;
-		password = global_db_password;
-		hostname = global_db_hostname;
-		port     = global_db_port;
-		database = global_db_database;
-		codepage = global_codepage;
-	}
-
-	sql_handle = Sql_Malloc();
-
-	if( SQL_ERROR == Sql_Connect(sql_handle, username, password, hostname, port, database) )
-	{
-		Sql_ShowDebug(sql_handle);
-		Sql_Free(sql_handle);
-		exit(EXIT_FAILURE);
-	}
-
-	if( codepage[0] != '\0' && SQL_ERROR == Sql_SetEncoding(sql_handle, codepage) )
-		Sql_ShowDebug(sql_handle);
-
-	enabled = true;
-
-	return true;
-}
-
-bool loginlog_final(void)
-{
-	Sql_Free(sql_handle);
-	sql_handle = NULL;
-	return true;
-}
-
-bool loginlog_config_read(const char* key, const char* value)
-{
+/**
+ * Read configuration options.
+ * @param key: config keyword
+ * @param value: config value for keyword
+ * @return true if successful, false if config not complete or server already running
+ */
+bool loginlog_config_read(const char* key, const char* value) {
 	const char* signature;
 
 	signature = "sql.";
@@ -182,3 +148,67 @@ bool loginlog_config_read(const char* key, const char* value)
 
 	return true;
 }
+
+
+/// Constructor destructor
+
+/**
+ * Initialize the module.
+ * Launched at login-serv start, create db or other long scope variable here.
+ * @return true if success else exit execution
+ */
+bool loginlog_init(void) {
+	const char* username;
+	const char* password;
+	const char* hostname;
+	uint16      port;
+	const char* database;
+	const char* codepage;
+
+	if( log_db_hostname[0] != '\0' )
+	{// local settings
+		username = log_db_username;
+		password = log_db_password;
+		hostname = log_db_hostname;
+		port     = log_db_port;
+		database = log_db_database;
+		codepage = log_codepage;
+	}
+	else
+	{// global settings
+		username = global_db_username;
+		password = global_db_password;
+		hostname = global_db_hostname;
+		port     = global_db_port;
+		database = global_db_database;
+		codepage = global_codepage;
+	}
+
+	sql_handle = Sql_Malloc();
+
+	if( SQL_ERROR == Sql_Connect(sql_handle, username, password, hostname, port, database) )
+	{
+		Sql_ShowDebug(sql_handle);
+		Sql_Free(sql_handle);
+		exit(EXIT_FAILURE);
+	}
+
+	if( codepage[0] != '\0' && SQL_ERROR == Sql_SetEncoding(sql_handle, codepage) )
+		Sql_ShowDebug(sql_handle);
+
+	enabled = true;
+
+	return true;
+}
+
+
+/**
+ * Handler to cleanup module, called when login-server stops.
+ * Currently closing sql connection to log schema.
+ * @return true success
+ */
+bool loginlog_final(void) {
+	Sql_Free(sql_handle);
+	sql_handle = NULL;
+	return true;
+}

+ 49 - 3
src/login/loginlog.h

@@ -1,15 +1,61 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
+/**
+ * @file loginlog.h
+ * Module purpose is to register (log) events into a file or sql database.
+ * Licensed under GNU GPL.
+ *  For more information, see LICENCE in the main folder.
+ * @author Athena Dev Teams rev < 15k
+ * @author rAthena Dev Team
+ */
 
 #ifndef __LOGINLOG_H_INCLUDED__
 #define __LOGINLOG_H_INCLUDED__
 
+#ifdef	__cplusplus
+extern "C" {
+#endif
 
+/**
+ * Get the number of failed login attempts by the ip in the last minutes.
+ * @param ip: ip to search attempt from
+ * @param minutes: intervall to search
+ * @return number of failed attempts
+ */
 unsigned long loginlog_failedattempts(uint32 ip, unsigned int minutes);
+
+/**
+ * Records an event in the login log.
+ * @param ip:
+ * @param username:
+ * @param rcode:
+ * @param message:
+ */
 void login_log(uint32 ip, const char* username, int rcode, const char* message);
+
+/**
+ * Read configuration options.
+ * @param key: config keyword
+ * @param value: config value for keyword
+ * @return true if successful, false if config not complete or server already running
+ */
+bool loginlog_config_read(const char* w1, const char* w2);
+
+
+/**
+ * Initialize the module.
+ * Launched at login-serv start, create db or other long scope variable here.
+ * @return true if success else exit execution
+ */
 bool loginlog_init(void);
+
+/**
+ * Handler to cleanup module, called when login-server stops.
+ * atm closing sql connection to log schema
+ * @return true success
+ */
 bool loginlog_final(void);
-bool loginlog_config_read(const char* w1, const char* w2);
 
+#ifdef	__cplusplus
+}
+#endif
 
 #endif // __LOGINLOG_H_INCLUDED__

+ 9 - 3
src/login/sql/CMakeLists.txt

@@ -8,13 +8,19 @@ set( SQL_LOGIN_HEADERS
 	"${SQL_LOGIN_SOURCE_DIR}/account.h"
 	"${SQL_LOGIN_SOURCE_DIR}/ipban.h"
 	"${SQL_LOGIN_SOURCE_DIR}/login.h"
+	"${SQL_LOGIN_SOURCE_DIR}/loginclif.h"
+	"${SQL_LOGIN_SOURCE_DIR}/loginchrif.h"
+	"${SQL_LOGIN_SOURCE_DIR}/logincnslif.h"
 	"${SQL_LOGIN_SOURCE_DIR}/loginlog.h"
 	)
 set( SQL_LOGIN_SOURCES
-	"${SQL_LOGIN_SOURCE_DIR}/account_sql.c"
-	"${SQL_LOGIN_SOURCE_DIR}/ipban_sql.c"
+	"${SQL_LOGIN_SOURCE_DIR}/account.c"
+	"${SQL_LOGIN_SOURCE_DIR}/ipban.c"
 	"${SQL_LOGIN_SOURCE_DIR}/login.c"
-	"${SQL_LOGIN_SOURCE_DIR}/loginlog_sql.c"
+	"${SQL_LOGIN_SOURCE_DIR}/loginclif.c"
+	"${SQL_LOGIN_SOURCE_DIR}/loginchrif.c"
+	"${SQL_LOGIN_SOURCE_DIR}/logincnslif.c"
+	"${SQL_LOGIN_SOURCE_DIR}/loginlog.c"
 	)
 set( DEPENDENCIES common_sql )
 set( LIBRARIES ${GLOBAL_LIBRARIES} )

+ 9 - 3
vcproj-10/login-server_sql.vcxproj

@@ -147,6 +147,9 @@
     <ClInclude Include="..\src\login\account.h" />
     <ClInclude Include="..\src\login\ipban.h" />
     <ClInclude Include="..\src\login\login.h" />
+    <ClInclude Include="..\src\login\loginchrif.h" />
+    <ClInclude Include="..\src\login\loginclif.h" />
+    <ClInclude Include="..\src\login\logincnslif.h" />
     <ClInclude Include="..\src\login\loginlog.h" />
     <ClInclude Include="..\src\common\cbasetypes.h" />
     <ClInclude Include="..\src\common\core.h" />
@@ -178,10 +181,13 @@
     <ClCompile Include="..\src\common\mutex.c" />
     <ClCompile Include="..\src\common\raconf.c" />
     <ClCompile Include="..\src\common\thread.c" />
-    <ClCompile Include="..\src\login\account_sql.c" />
-    <ClCompile Include="..\src\login\ipban_sql.c" />
+    <ClCompile Include="..\src\login\account.c" />
+    <ClCompile Include="..\src\login\ipban.c" />
     <ClCompile Include="..\src\login\login.c" />
-    <ClCompile Include="..\src\login\loginlog_sql.c" />
+    <ClCompile Include="..\src\login\loginchrif.c" />
+    <ClCompile Include="..\src\login\loginclif.c" />
+    <ClCompile Include="..\src\login\logincnslif.c" />
+    <ClCompile Include="..\src\login\loginlog.c" />
     <ClCompile Include="..\src\common\core.c" />
     <ClCompile Include="..\src\common\db.c" />
     <ClCompile Include="..\src\common\ers.c" />

+ 9 - 3
vcproj-12/login-server_sql.vcxproj

@@ -151,6 +151,9 @@
     <ClInclude Include="..\src\login\ipban.h" />
     <ClInclude Include="..\src\login\login.h" />
     <ClInclude Include="..\src\login\loginlog.h" />
+    <ClInclude Include="..\src\login\loginchrif.h" />
+    <ClInclude Include="..\src\login\loginclif.h" />
+    <ClInclude Include="..\src\login\logincnslif.h" />
     <ClInclude Include="..\src\common\cbasetypes.h" />
     <ClInclude Include="..\src\common\core.h" />
     <ClInclude Include="..\src\common\conf.h" />
@@ -181,10 +184,13 @@
     <ClCompile Include="..\src\common\mutex.c" />
     <ClCompile Include="..\src\common\raconf.c" />
     <ClCompile Include="..\src\common\thread.c" />
-    <ClCompile Include="..\src\login\account_sql.c" />
-    <ClCompile Include="..\src\login\ipban_sql.c" />
+    <ClCompile Include="..\src\login\account.c" />
+    <ClCompile Include="..\src\login\ipban.c" />
     <ClCompile Include="..\src\login\login.c" />
-    <ClCompile Include="..\src\login\loginlog_sql.c" />
+    <ClCompile Include="..\src\login\loginlog.c" />
+    <ClCompile Include="..\src\login\loginchrif.c" />
+    <ClCompile Include="..\src\login\loginclif.c" />
+    <ClCompile Include="..\src\login\logincnslif.c" />
     <ClCompile Include="..\src\common\core.c" />
     <ClCompile Include="..\src\common\conf.c" />
     <ClCompile Include="..\src\common\db.c" />

+ 27 - 3
vcproj-9/login-server_sql.vcproj

@@ -211,7 +211,7 @@
 				>
 			</File>
 			<File
-				RelativePath="..\src\login\account_sql.c"
+				RelativePath="..\src\login\account.c"
 				>
 			</File>
 			<File
@@ -219,7 +219,7 @@
 				>
 			</File>
 			<File
-				RelativePath="..\src\login\ipban_sql.c"
+				RelativePath="..\src\login\ipban.c"
 				>
 			</File>
 			<File
@@ -230,12 +230,36 @@
 				RelativePath="..\src\login\login.h"
 				>
 			</File>
+			<File
+				RelativePath="..\src\login\loginchrif.c"
+				>
+			</File>
+			<File
+				RelativePath="..\src\login\loginchrif.h"
+				>
+			</File>
+			<File
+				RelativePath="..\src\login\loginclif.c"
+				>
+			</File>
+			<File
+				RelativePath="..\src\login\loginclif.h"
+				>
+			</File>
+			<File
+				RelativePath="..\src\login\logincnslif.c"
+				>
+			</File>
+			<File
+				RelativePath="..\src\login\logincnslif.h"
+				>
+			</File>
 			<File
 				RelativePath="..\src\login\loginlog.h"
 				>
 			</File>
 			<File
-				RelativePath="..\src\login\loginlog_sql.c"
+				RelativePath="..\src\login\loginlog.c"
 				>
 			</File>
 		</Filter>

Some files were not shown because too many files changed in this diff