root/src/login_sql/login.c

Revision 1, 63.4 kB (checked in by jinshiro, 17 years ago)
Line 
1// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
2// For more information, see LICENCE in the main folder
3
4#include "../common/cbasetypes.h"
5#include "../common/mmo.h"
6#include "../common/core.h"
7#include "../common/socket.h"
8#include "../common/db.h"
9#include "../common/timer.h"
10#include "../common/malloc.h"
11#include "../common/strlib.h"
12#include "../common/showmsg.h"
13#include "../common/version.h"
14#include "../common/md5calc.h"
15#include "../common/sql.h"
16#include "login.h"
17
18#include <stdio.h>
19#include <stdlib.h>
20#include <string.h>
21#include <sys/stat.h> // for stat/lstat/fstat
22
23struct Login_Config login_config;
24
25int login_fd; // login server socket
26#define MAX_SERVERS 30
27struct mmo_char_server server[MAX_SERVERS]; // char server data
28
29#define sex_num2str(num) ( (num ==  0  ) ? 'F' : (num ==  1  ) ? 'M' : 'S' )
30#define sex_str2num(str) ( (str == 'F' ) ?  0  : (str == 'M' ) ?  1  :  2  )
31
32// Advanced subnet check [LuzZza]
33struct s_subnet {
34        uint32 mask;
35        uint32 char_ip;
36        uint32 map_ip;
37} subnet[16];
38
39int subnet_count = 0;
40
41// GM account management
42struct gm_account* gm_account_db = NULL;
43unsigned int GM_num = 0; // number of gm accounts
44
45//Account registration flood protection [Kevin]
46int allowed_regs = 1;
47int time_allowed = 10; //in seconds
48unsigned int new_reg_tick = 0;
49
50
51// data handling (SQL)
52Sql* sql_handle;
53
54// database parameters
55uint16 login_server_port = 3306;
56char login_server_ip[32] = "127.0.0.1";
57char login_server_id[32] = "ragnarok";
58char login_server_pw[32] = "ragnarok";
59char login_server_db[32] = "ragnarok";
60char default_codepage[32] = "";
61
62char login_db[256] = "login";
63char loginlog_db[256] = "loginlog";
64char reg_db[256] = "global_reg_value";
65
66// added to help out custom login tables, without having to recompile
67// source so options are kept in the login_athena.conf or the inter_athena.conf
68char login_db_account_id[256] = "account_id";
69char login_db_userid[256] = "userid";
70char login_db_user_pass[256] = "user_pass";
71char login_db_level[256] = "level";
72
73
74//-----------------------------------------------------
75// Auth database
76//-----------------------------------------------------
77#define AUTH_TIMEOUT 30000
78
79struct auth_node {
80        int account_id;
81        uint32 login_id1;
82        uint32 login_id2;
83        uint32 ip;
84        char sex;
85};
86
87static DBMap* auth_db; // int account_id -> struct auth_node*
88
89//-----------------------------------------------------
90// Online User Database [Wizputer]
91//-----------------------------------------------------
92
93struct online_login_data {
94        int account_id;
95        int waiting_disconnect;
96        int char_server;
97};
98
99static DBMap* online_db; // int account_id -> struct online_login_data*
100static int waiting_disconnect_timer(int tid, unsigned int tick, int id, intptr data);
101
102static void* create_online_user(DBKey key, va_list args)
103{
104        struct online_login_data* p;
105        CREATE(p, struct online_login_data, 1);
106        p->account_id = key.i;
107        p->char_server = -1;
108        p->waiting_disconnect = -1;
109        return p;
110}
111
112struct online_login_data* add_online_user(int char_server, int account_id)
113{
114        struct online_login_data* p;
115        if( !login_config.online_check )
116                return NULL;
117        p = (struct online_login_data*)idb_ensure(online_db, account_id, create_online_user);
118        p->char_server = char_server;
119        if( p->waiting_disconnect != -1 )
120        {
121                delete_timer(p->waiting_disconnect, waiting_disconnect_timer);
122                p->waiting_disconnect = -1;
123        }
124        return p;
125}
126
127void remove_online_user(int account_id)
128{
129        struct online_login_data* p;
130        if( !login_config.online_check )
131                return;
132        p = (struct online_login_data*)idb_get(online_db, account_id);
133        if( p == NULL )
134                return;
135        if( p->waiting_disconnect != -1 )
136                delete_timer(p->waiting_disconnect, waiting_disconnect_timer);
137
138        idb_remove(online_db, account_id);
139}
140
141static int waiting_disconnect_timer(int tid, unsigned int tick, int id, intptr data)
142{
143        struct online_login_data* p = (struct online_login_data*)idb_get(online_db, id);
144        if( p != NULL && p->waiting_disconnect == tid && p->account_id == id )
145        {
146                p->waiting_disconnect = -1;
147                remove_online_user(id);
148                idb_remove(auth_db, id);
149        }
150        return 0;
151}
152
153//--------------------------------------------------------------------
154// Packet send to all char-servers, except one (wos: without our self)
155//--------------------------------------------------------------------
156int charif_sendallwos(int sfd, uint8* buf, size_t len)
157{
158        int i, c;
159
160        for( i = 0, c = 0; i < MAX_SERVERS; ++i )
161        {
162                int fd = server[i].fd;
163                if( session_isValid(fd) && fd != sfd )
164                {
165                        WFIFOHEAD(fd,len);
166                        memcpy(WFIFOP(fd,0), buf, len);
167                        WFIFOSET(fd,len);
168                        ++c;
169                }
170        }
171
172        return c;
173}
174
175//-----------------------------------------------------
176// Read GM accounts
177//-----------------------------------------------------
178void read_gm_account(void)
179{
180        if( !login_config.login_gm_read )
181                return;// char server's job
182
183        if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `%s`,`%s` FROM `%s` WHERE `%s` > '0'", login_db_account_id, login_db_level, login_db, login_db_level) )
184        {
185                Sql_ShowDebug(sql_handle);
186                return;// Failed to read GM list!
187        }
188
189        RECREATE(gm_account_db, struct gm_account, (size_t)Sql_NumRows(sql_handle));
190
191        for( GM_num = 0; SQL_SUCCESS == Sql_NextRow(sql_handle); ++GM_num )
192        {
193                char* account;
194                char* level;
195
196                Sql_GetData(sql_handle, 0, &account, NULL);
197                Sql_GetData(sql_handle, 1, &level, NULL);
198
199                gm_account_db[GM_num].account_id = atoi(account);
200                gm_account_db[GM_num].level = atoi(level);
201        }
202
203        Sql_FreeResult(sql_handle);
204}
205
206//-----------------------------------------------------
207// Send GM accounts to one or all char-servers
208//-----------------------------------------------------
209void send_GM_accounts(int fd)
210{
211        unsigned int i;
212        uint8 buf[32767];
213        uint16 len;
214
215        if( !login_config.login_gm_read )
216                return;
217
218        len = 4;
219        WBUFW(buf,0) = 0x2732;
220        for( i = 0; i < GM_num; ++i )
221        {
222                // send only existing accounts. We can not create a GM account when server is online.
223                if( gm_account_db[i].level > 0 )
224                {
225                        WBUFL(buf,len) = gm_account_db[i].account_id;
226                        WBUFB(buf,len+4) = (uint8)gm_account_db[i].level;
227                        len += 5;
228                        if( len >= 32000 )
229                        {
230                                ShowWarning("send_GM_accounts: Too many accounts! Only %d out of %d were sent.\n", i, GM_num);
231                                break;
232                        }
233                }
234        }
235
236        WBUFW(buf,2) = len;
237        if( fd == -1 )// send to all charservers
238                charif_sendallwos(-1, buf, len);
239        else
240        {// send only to target
241                WFIFOHEAD(fd,len);
242                memcpy(WFIFOP(fd,0), buf, len);
243                WFIFOSET(fd,len);
244        }
245
246        return;
247}
248
249/*=============================================
250 * Does a mysql_ping to all connection handles
251 *---------------------------------------------*/
252int login_sql_ping(int tid, unsigned int tick, int id, intptr data) 
253{
254        ShowInfo("Pinging SQL server to keep connection alive...\n");
255        Sql_Ping(sql_handle);
256        return 0;
257}
258
259int sql_ping_init(void)
260{
261        uint32 connection_timeout, connection_ping_interval;
262
263        // set a default value first
264        connection_timeout = 28800; // 8 hours
265
266        // ask the mysql server for the timeout value
267        if( SQL_SUCCESS == Sql_GetTimeout(sql_handle, &connection_timeout) && connection_timeout < 60 )
268                connection_timeout = 60;
269
270        // establish keepalive
271        connection_ping_interval = connection_timeout - 30; // 30-second reserve
272        add_timer_func_list(login_sql_ping, "login_sql_ping");
273        add_timer_interval(gettick() + connection_ping_interval*1000, login_sql_ping, 0, 0, connection_ping_interval*1000);
274
275        return 0;
276}
277
278//-----------------------------------------------------
279// Read Account database - mysql db
280//-----------------------------------------------------
281int mmo_auth_sqldb_init(void)
282{
283        ShowStatus("Login server init....\n");
284
285        sql_handle = Sql_Malloc();
286
287        // DB connection start
288        ShowStatus("Connect Login Database Server....\n");
289        if( SQL_ERROR == Sql_Connect(sql_handle, login_server_id, login_server_pw, login_server_ip, login_server_port, login_server_db) )
290        {
291                Sql_ShowDebug(sql_handle);
292                Sql_Free(sql_handle);
293                exit(EXIT_FAILURE);
294        }
295        else
296        {
297                ShowStatus("Connect success!\n");
298        }
299
300        if( default_codepage[0] != '\0' && SQL_ERROR == Sql_SetEncoding(sql_handle, default_codepage) )
301                Sql_ShowDebug(sql_handle);
302
303        if( login_config.log_login && SQL_ERROR == Sql_Query(sql_handle, "INSERT DELAYED INTO `%s` (`time`,`ip`,`user`,`rcode`,`log`) VALUES (NOW(), '0', 'lserver','100','login server started')", loginlog_db) )
304                Sql_ShowDebug(sql_handle);
305
306        sql_ping_init();
307
308        return 0;
309}
310
311
312//-----------------------------------------------------
313// close DB
314//-----------------------------------------------------
315void mmo_db_close(void)
316{
317        int i, fd;
318
319        //set log.
320        if( login_config.log_login && SQL_ERROR == Sql_Query(sql_handle, "INSERT DELAYED INTO `%s`(`time`,`ip`,`user`,`rcode`,`log`) VALUES (NOW(), '0', 'lserver','100', 'login server shutdown')", loginlog_db) )
321                Sql_ShowDebug(sql_handle);
322
323        for( i = 0; i < MAX_SERVERS; ++i )
324        {
325                fd = server[i].fd;
326                if( session_isValid(fd) )
327                {// Clean only data related to servers we are connected to. [Skotlex]
328                        if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `sstatus` WHERE `index` = '%d'", i) )
329                                Sql_ShowDebug(sql_handle);
330                        do_close(fd);
331                }
332        }
333        Sql_Free(sql_handle);
334        sql_handle = NULL;
335        ShowStatus("close DB connect....\n");
336        if( login_fd > 0 )
337                do_close(login_fd);
338}
339
340//-----------------------------------------------------
341// periodic ip address synchronization
342//-----------------------------------------------------
343static int sync_ip_addresses(int tid, unsigned int tick, int id, intptr data)
344{
345        uint8 buf[2];
346        ShowInfo("IP Sync in progress...\n");
347        WBUFW(buf,0) = 0x2735;
348        charif_sendallwos(-1, buf, 2);
349        return 0;
350}
351
352//-----------------------------------------------------
353// encrypted/unencrypted password check
354//-----------------------------------------------------
355bool check_encrypted(const char* str1, const char* str2, const char* passwd)
356{
357        char md5str[64], md5bin[32];
358
359        snprintf(md5str, sizeof(md5str), "%s%s", str1, str2);
360        md5str[sizeof(md5str)-1] = '\0';
361        MD5_String2binary(md5str, md5bin);
362
363        return (0==memcmp(passwd, md5bin, 16));
364}
365
366bool check_password(struct login_session_data* ld, int passwdenc, const char* passwd, const char* refpass)
367{       
368        if(passwdenc == 0)
369        {
370                return (0==strcmp(passwd, refpass));
371        }
372        else if (ld)
373        {
374                // password mode set to 1 -> (md5key, refpass) enable with <passwordencrypt></passwordencrypt>
375                // password mode set to 2 -> (refpass, md5key) enable with <passwordencrypt2></passwordencrypt2>
376               
377                return ((passwdenc&0x01) && check_encrypted(ld->md5key, refpass, passwd)) ||
378                       ((passwdenc&0x02) && check_encrypted(refpass, ld->md5key, passwd));
379        }
380        return false;
381}
382
383
384//-----------------------------------------------------
385// Make new account
386//-----------------------------------------------------
387int mmo_auth_new(struct mmo_account* account)
388{
389        static int num_regs = 0; // registration counter
390        unsigned int tick = gettick();
391
392        char md5buf[32+1];
393        time_t expiration_time = 0;
394        SqlStmt* stmt;
395        int result = 0;
396
397        //Account Registration Flood Protection by [Kevin]
398        if( DIFF_TICK(tick, new_reg_tick) < 0 && num_regs >= allowed_regs )
399        {
400                ShowNotice("Account registration denied (registration limit exceeded)\n");
401                return 3;
402        }
403
404        // check if the account doesn't exist already
405        stmt = SqlStmt_Malloc(sql_handle);
406        if( SQL_SUCCESS != SqlStmt_Prepare(stmt, "SELECT `%s` FROM `%s` WHERE `userid` = ?", login_db_userid, login_db)
407        ||  SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_STRING, account->userid, strnlen(account->userid, NAME_LENGTH))
408        ||  SQL_SUCCESS != SqlStmt_Execute(stmt) )
409        {
410                SqlStmt_ShowDebug(stmt);
411                result = 1;// error
412        }
413        else if( SqlStmt_NumRows(stmt) > 0 )
414                result = 1;// username already taken
415        SqlStmt_Free(stmt);
416        if( result )
417                return result;// error or incorrect user
418
419        if( login_config.start_limited_time != -1 )
420                expiration_time = time(NULL) + login_config.start_limited_time;
421
422        // insert new entry into db
423        //TODO: error checking
424        stmt = SqlStmt_Malloc(sql_handle);
425        SqlStmt_Prepare(stmt, "INSERT INTO `%s` (`%s`, `%s`, `sex`, `email`, `connect_until`) VALUES (?, ?, '%c', 'a@a.com', '%d')", login_db, login_db_userid, login_db_user_pass, account->sex, expiration_time);
426        SqlStmt_BindParam(stmt, 0, SQLDT_STRING, account->userid, strnlen(account->userid, NAME_LENGTH));
427        if( login_config.use_md5_passwds )
428        {
429                MD5_String(account->pass, md5buf);
430                SqlStmt_BindParam(stmt, 1, SQLDT_STRING, md5buf, 32);
431        }
432        else
433                SqlStmt_BindParam(stmt, 1, SQLDT_STRING, account->pass, strnlen(account->pass, NAME_LENGTH));
434        SqlStmt_Execute(stmt);
435
436        ShowNotice("Account creation (account %s, id: %d, pass: %s, sex: %c)\n", account->userid, (int)SqlStmt_LastInsertId(stmt), account->pass, account->sex);
437        SqlStmt_Free(stmt);
438
439        if( DIFF_TICK(tick, new_reg_tick) > 0 )
440        {// Update the registration check.
441                num_regs = 0;
442                new_reg_tick = tick + time_allowed*1000;
443        }
444        ++num_regs;
445
446        return 0;
447}
448
449
450//-----------------------------------------------------
451// Check/authentication of a connection
452//-----------------------------------------------------
453int mmo_auth(struct login_session_data* sd)
454{
455        time_t unban_time;
456        char esc_userid[NAME_LENGTH*2+1];// escaped username
457        char user_password[256], password[256];
458        long expiration_time;
459        int state;
460        size_t len;
461        char* data;
462
463        char ip[16];
464        ip2str(session[sd->fd]->client_addr, ip);
465
466        // DNS Blacklist check
467        if( login_config.use_dnsbl )
468        {
469                char r_ip[16];
470                char ip_dnsbl[256];
471                char* dnsbl_serv;
472                bool matched = false;
473                uint8* sin_addr = (uint8*)&session[sd->fd]->client_addr;
474
475                sprintf(r_ip, "%u.%u.%u.%u", sin_addr[0], sin_addr[1], sin_addr[2], sin_addr[3]);
476
477                for( dnsbl_serv = strtok(login_config.dnsbl_servs,","); !matched && dnsbl_serv != NULL; dnsbl_serv = strtok(NULL,",") )
478                {
479                        sprintf(ip_dnsbl, "%s.%s", r_ip, dnsbl_serv);
480                        if( host2ip(ip_dnsbl) )
481                                matched = true;
482                }
483
484                if( matched )
485                {
486                        ShowInfo("DNSBL: (%s) Blacklisted. User Kicked.\n", r_ip);
487                        return 3;
488                }
489        }
490
491        //Client Version check
492        if( login_config.check_client_version && sd->version != login_config.client_version_to_connect )
493                return 5;
494
495        len = strnlen(sd->userid, NAME_LENGTH);
496
497        // Account creation with _M/_F
498        if( login_config.new_account_flag )
499        {
500                if( len > 2 && strnlen(sd->passwd, NAME_LENGTH) > 0 && // valid user and password lengths
501                        sd->passwdenc == 0 && // unencoded password
502                        sd->userid[len-2] == '_' && memchr("FfMm", sd->userid[len-1], 4) ) // _M/_F suffix
503                {
504                        struct mmo_account acc;
505                        int result;
506
507                        len -= 2;
508                        sd->userid[len] = '\0';
509
510                        memset(&acc, '\0', sizeof(acc));
511                        safestrncpy(acc.userid, sd->userid, NAME_LENGTH);
512                        safestrncpy(acc.pass, sd->passwd, NAME_LENGTH);
513                        safestrncpy(acc.email, "a@a.com", sizeof(acc.email));
514                        acc.sex = TOUPPER(sd->userid[len+1]);
515
516                        result = mmo_auth_new(&acc);
517                        if( result )
518                                return result;// Failed to make account. [Skotlex].
519                }
520        }
521
522        // escape username
523        Sql_EscapeStringLen(sql_handle, esc_userid, sd->userid, len);
524
525        // retrieve login entry for the specified username
526        if( SQL_ERROR == Sql_Query(sql_handle,
527                "SELECT `%s`,`%s`,`lastlogin`,`sex`,`connect_until`,`ban_until`,`state`,`%s` FROM `%s` WHERE `%s`= %s '%s'",
528                login_db_account_id, login_db_user_pass, login_db_level,
529                login_db, login_db_userid, (login_config.case_sensitive ? "BINARY" : ""), esc_userid) )
530                Sql_ShowDebug(sql_handle);
531
532        if( Sql_NumRows(sql_handle) == 0 ) // no such entry
533        {
534                ShowNotice("auth failed: no such account '%s'\n", esc_userid);
535                Sql_FreeResult(sql_handle);
536                return 0;
537        }
538
539        Sql_NextRow(sql_handle); //TODO: error checking?
540
541        Sql_GetData(sql_handle, 0, &data, NULL); sd->account_id = atoi(data);
542        Sql_GetData(sql_handle, 1, &data, &len); safestrncpy(password, data, sizeof(password));
543        Sql_GetData(sql_handle, 2, &data, NULL); safestrncpy(sd->lastlogin, data, sizeof(sd->lastlogin));
544        Sql_GetData(sql_handle, 3, &data, NULL); sd->sex = *data;
545        Sql_GetData(sql_handle, 4, &data, NULL); expiration_time = atol(data);
546        Sql_GetData(sql_handle, 5, &data, NULL); unban_time = atol(data);
547        Sql_GetData(sql_handle, 6, &data, NULL); state = atoi(data);
548        Sql_GetData(sql_handle, 7, &data, NULL); sd->level = atoi(data);
549        if( len > sizeof(password) - 1 )
550                ShowDebug("mmo_auth: password buffer is too small (len=%u,buflen=%u)\n", len, sizeof(password));
551        if( sd->level > 99 )
552                sd->level = 99;
553
554        Sql_FreeResult(sql_handle);
555
556        if( login_config.use_md5_passwds )
557                MD5_String(sd->passwd, user_password);
558        else
559                safestrncpy(user_password, sd->passwd, NAME_LENGTH);
560
561        if( !check_password(sd, sd->passwdenc, user_password, password) )
562        {
563                ShowInfo("Invalid password (account: '%s', pass: '%s', received pass: '%s', ip: %s)\n",
564                        esc_userid, password, (sd->passwdenc) ? "[MD5]" : user_password, ip);
565                return 1; // 1 = Incorrect Password
566        }
567
568        if( expiration_time != 0 && expiration_time < time(NULL) )
569                return 2; // 2 = This ID is expired
570
571        if( unban_time != 0 && unban_time > time(NULL) )
572                return 6; // 6 = Your are Prohibited to log in until %s
573
574        if( state )
575        {
576                ShowInfo("Connection refused (account: %s, pass: %s, state: %d, ip: %s)\n", sd->userid, sd->passwd, state, ip);
577                return state - 1;
578        }
579
580        sd->login_id1 = rand();
581        sd->login_id2 = rand();
582
583        if( sd->sex != 'S' && sd->account_id < START_ACCOUNT_NUM )
584                ShowWarning("Account %s has account id %d! Account IDs must be over %d to work properly!\n", sd->userid, sd->account_id, START_ACCOUNT_NUM);
585
586        if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `lastlogin` = NOW(), `logincount`=`logincount`+1, `last_ip`='%s', `ban_until`='0', `state`='0' WHERE `%s` = '%d'",
587                login_db, ip, login_db_account_id, sd->account_id) )
588                Sql_ShowDebug(sql_handle);
589
590        return -1; // account OK
591}
592
593static int online_db_setoffline(DBKey key, void* data, va_list ap)
594{
595        struct online_login_data* p = (struct online_login_data*)data;
596        int server = va_arg(ap, int);
597        if( server == -1 )
598        {
599                p->char_server = -1;
600                if( p->waiting_disconnect != -1 )
601                {
602                        delete_timer(p->waiting_disconnect, waiting_disconnect_timer);
603                        p->waiting_disconnect = -1;
604                }
605        }
606        else if( p->char_server == server )
607                p->char_server = -2; //Char server disconnected.
608        return 0;
609}
610
611//--------------------------------
612// Packet parsing for char-servers
613//--------------------------------
614int parse_fromchar(int fd)
615{
616        unsigned int i;
617        int id;
618        uint32 ipl;
619        char ip[16];
620
621        ARR_FIND( 0, MAX_SERVERS, id, server[id].fd == fd );
622        if( id == MAX_SERVERS )
623        {// not a char server
624                set_eof(fd);
625                do_close(fd);
626                return 0;
627        }
628
629        if( session[fd]->flag.eof )
630        {
631                ShowStatus("Char-server '%s' has disconnected.\n", server[id].name);
632                online_db->foreach(online_db, online_db_setoffline, id); //Set all chars from this char server to offline.
633                if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `sstatus` WHERE `index`='%d'", id) )
634                        Sql_ShowDebug(sql_handle);
635                memset(&server[id], 0, sizeof(struct mmo_char_server));
636                server[id].fd = -1;
637                do_close(fd);
638                return 0;
639        }
640
641        ipl = server[id].ip;
642        ip2str(ipl, ip);
643
644        while( RFIFOREST(fd) >= 2 )
645        {
646                uint16 command = RFIFOW(fd,0);
647
648                switch( command )
649                {
650
651                case 0x2709: // request from map-server via char-server to reload GM accounts
652                        RFIFOSKIP(fd,2);
653                        ShowStatus("Char-server '%s': Request to re-load GM configuration file (ip: %s).\n", server[id].name, ip);
654                        if( login_config.log_login )
655                        {
656                                if( SQL_ERROR == Sql_Query(sql_handle, "INSERT DELAYED INTO `%s`(`time`,`ip`,`user`,`log`) VALUES (NOW(), '%u', '%s', 'GM reload request')", loginlog_db, ipl, server[id].name) )
657                                        Sql_ShowDebug(sql_handle);
658                        }
659                        read_gm_account();
660                        // send GM accounts to all char-servers
661                        send_GM_accounts(-1);
662                break;
663
664                case 0x2712: // request from char-server to authenticate an account
665                        if( RFIFOREST(fd) < 19 )
666                                return 0;
667                {
668                        struct auth_node* node;
669
670                        int account_id = RFIFOL(fd,2);
671                        uint32 login_id1 = RFIFOL(fd,6);
672                        uint32 login_id2 = RFIFOL(fd,10);
673                        char sex = sex_num2str(RFIFOB(fd,14));
674                        uint32 ip_ = ntohl(RFIFOL(fd,15));
675                        RFIFOSKIP(fd,19);
676
677                        node = (struct auth_node*)idb_get(auth_db, account_id);
678                        if( node != NULL &&
679                            node->account_id == account_id &&
680                                node->login_id1  == login_id1 &&
681                                node->login_id2  == login_id2 &&
682                                node->sex        == sex &&
683                                node->ip         == ip_ )
684                        {// found
685                                uint32 expiration_time;
686                                char email[40];
687
688                                // each auth entry can only be used once
689                                idb_remove(auth_db, account_id);
690
691                                // retrieve email and account expiration time
692                                if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `email`,`connect_until` FROM `%s` WHERE `%s`='%d'", login_db, login_db_account_id, account_id) )
693                                        Sql_ShowDebug(sql_handle);
694                                if( SQL_SUCCESS == Sql_NextRow(sql_handle) )
695                                {
696                                        char* data = NULL;
697                                        size_t len = 0;
698
699                                        Sql_GetData(sql_handle, 0, &data, &len); safestrncpy(email, data, sizeof(email));
700                                        Sql_GetData(sql_handle, 1, &data, NULL); expiration_time = (uint32)strtoul(data, NULL, 10);
701                                        if( len > sizeof(email) )
702                                                ShowDebug("parse_fromchar:0x2712: email is too long (len=%u,maxlen=%u)\n", len, sizeof(email));
703
704                                        Sql_FreeResult(sql_handle);
705                                }
706                                else
707                                {
708                                        memset(email, 0, sizeof(email));
709                                        expiration_time = 0;
710                                }
711
712                                // send ack
713                                WFIFOHEAD(fd,59);
714                                WFIFOW(fd,0) = 0x2713;
715                                WFIFOL(fd,2) = account_id;
716                                WFIFOL(fd,6) = login_id1;
717                                WFIFOL(fd,10) = login_id2;
718                                WFIFOB(fd,14) = 0;
719                                memcpy(WFIFOP(fd,15), email, 40);
720                                WFIFOL(fd,55) = expiration_time;
721                                WFIFOSET(fd,59);
722                        }
723                        else
724                        {// authentication not found
725                                ShowStatus("Char-server '%s': authentication of the account %d REFUSED (ip: %s).\n", server[id].name, account_id, ip);
726                                WFIFOHEAD(fd,59);
727                                WFIFOW(fd,0) = 0x2713;
728                                WFIFOL(fd,2) = account_id;
729                                WFIFOL(fd,6) = login_id1;
730                                WFIFOL(fd,10) = login_id2;
731                                WFIFOB(fd,14) = 1;
732                                // It is unnecessary to send email
733                                // It is unnecessary to send validity date of the account
734                                WFIFOSET(fd,59);
735                        }
736                }
737                break;
738
739                case 0x2714:
740                        if( RFIFOREST(fd) < 6 )
741                                return 0;
742                {
743                        int users = RFIFOL(fd,2);
744                        RFIFOSKIP(fd,6);
745
746                        // how many users on world? (update)
747                        if( server[id].users != users )
748                        {
749                                ShowStatus("set users %s : %d\n", server[id].name, users);
750
751                                server[id].users = users;
752                                if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `sstatus` SET `user` = '%d' WHERE `index` = '%d'", server[id].users, id) )
753                                        Sql_ShowDebug(sql_handle);
754                        }
755                }
756                break;
757
758                case 0x2716: // received an e-mail/limited time request, because a player comes back from a map-server to the char-server
759                        if( RFIFOREST(fd) < 6 )
760                                return 0;
761                {
762                        uint32 expiration_time = 0;
763                        char email[40] = "";
764
765                        int account_id = RFIFOL(fd,2);
766                        RFIFOSKIP(fd,6);
767
768                        if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `email`,`connect_until` FROM `%s` WHERE `%s`='%d'", login_db, login_db_account_id, account_id) )
769                                Sql_ShowDebug(sql_handle);
770                        else if( SQL_SUCCESS == Sql_NextRow(sql_handle) )
771                        {
772                                char* data;
773                                size_t len;
774
775                                Sql_GetData(sql_handle, 0, &data, &len);
776                                safestrncpy(email, data, sizeof(email));
777
778                                Sql_GetData(sql_handle, 1, &data, NULL);
779                                expiration_time = (uint32)strtoul(data, NULL, 10);
780
781                                Sql_FreeResult(sql_handle);
782                        }
783
784                        WFIFOHEAD(fd,50);
785                        WFIFOW(fd,0) = 0x2717;
786                        WFIFOL(fd,2) = account_id;
787                        safestrncpy((char*)WFIFOP(fd,6), email, 40);
788                        WFIFOL(fd,46) = expiration_time;
789                        WFIFOSET(fd,50);
790                }
791                break;
792
793                case 0x2719: // ping request from charserver
794                        if( RFIFOREST(fd) < 2 )
795                                return 0;
796                        RFIFOSKIP(fd,2);
797
798                        WFIFOHEAD(fd,2);
799                        WFIFOW(fd,0) = 0x2718;
800                        WFIFOSET(fd,2);
801                break;
802
803                // Map server send information to change an email of an account via char-server
804                case 0x2722:    // 0x2722 <account_id>.L <actual_e-mail>.40B <new_e-mail>.40B
805                        if (RFIFOREST(fd) < 86)
806                                return 0;
807                {
808                        char actual_email[40];
809                        char new_email[40];
810                        int account_id = RFIFOL(fd,2);
811                        safestrncpy(actual_email, (char*)RFIFOP(fd,6), 40);
812                        safestrncpy(new_email, (char*)RFIFOP(fd,46), 40);
813                        RFIFOSKIP(fd, 86);
814
815                        if( e_mail_check(actual_email) == 0 )
816                                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", server[id].name, account_id, ip);
817                        else if( e_mail_check(new_email) == 0 )
818                                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", server[id].name, account_id, ip);
819                        else if( strcmpi(new_email, "a@a.com") == 0 )
820                                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", server[id].name, account_id, ip);
821                        else if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `%s`,`email` FROM `%s` WHERE `%s` = '%d'", login_db_userid, login_db, login_db_account_id, account_id) )
822                                Sql_ShowDebug(sql_handle);
823                        else if( SQL_SUCCESS == Sql_NextRow(sql_handle) )
824                        {
825                                char* data;
826                                size_t len;
827
828                                Sql_GetData(sql_handle, 1, &data, &len);
829                                if( strncasecmp(data, actual_email, sizeof(actual_email)) == 0 )
830                                {
831                                        char esc_user_id[NAME_LENGTH*2+1];
832                                        char esc_new_email[sizeof(new_email)*2+1];
833
834                                        Sql_GetData(sql_handle, 0, &data, &len);
835                                        Sql_EscapeStringLen(sql_handle, esc_user_id, data, len);
836                                        Sql_EscapeStringLen(sql_handle, esc_new_email, new_email, strnlen(new_email, sizeof(new_email)));
837
838                                        if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `email` = '%s' WHERE `%s` = '%d'", login_db, esc_new_email, login_db_account_id, account_id) )
839                                                Sql_ShowDebug(sql_handle);
840                                        ShowInfo("Char-server '%s': Modify an e-mail on an account (@email GM command) (account: %d ('%s'), new e-mail: '%s', ip: %s).\n", server[id].name, account_id, esc_user_id, esc_new_email, ip);
841                                }
842                                Sql_FreeResult(sql_handle);
843                        }
844                }
845                break;
846
847                case 0x2724: // Receiving an account state update request from a map-server (relayed via char-server)
848                        if (RFIFOREST(fd) < 10)
849                                return 0;
850                {
851                        int account_id = RFIFOL(fd,2);
852                        int state = RFIFOL(fd,6);
853                        RFIFOSKIP(fd,10);
854
855                        if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `state` FROM `%s` WHERE `%s` = '%d'", login_db, login_db_account_id, account_id) )
856                                Sql_ShowDebug(sql_handle);
857                        else if( SQL_SUCCESS == Sql_NextRow(sql_handle) )
858                        {
859                                char* data;
860
861                                Sql_GetData(sql_handle, 0, &data, NULL);
862                                if( atoi(data) != state && state != 0 )
863                                {
864                                        uint8 buf[11];
865                                        WBUFW(buf,0) = 0x2731;
866                                        WBUFL(buf,2) = account_id;
867                                        WBUFB(buf,6) = 0; // 0: change of state, 1: ban
868                                        WBUFL(buf,7) = state; // status or final date of a banishment
869                                        charif_sendallwos(-1, buf, 11);
870                                }
871                                Sql_FreeResult(sql_handle);
872                        }
873
874                        if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `state` = '%d' WHERE `%s` = '%d'", login_db, state, login_db_account_id, account_id) )
875                                Sql_ShowDebug(sql_handle);
876                }
877                break;
878
879                case 0x2725: // Receiving of map-server via char-server a ban request
880                        if (RFIFOREST(fd) < 18)
881                                return 0;
882                {
883                        struct tm *tmtime;
884                        time_t tmptime = 0;
885                        time_t timestamp = time(NULL);
886
887                        int account_id = RFIFOL(fd,2);
888                        int year = (short)RFIFOW(fd,6);
889                        int month = (short)RFIFOW(fd,8);
890                        int mday = (short)RFIFOW(fd,10);
891                        int hour = (short)RFIFOW(fd,12);
892                        int min = (short)RFIFOW(fd,14);
893                        int sec = (short)RFIFOW(fd,16);
894                        RFIFOSKIP(fd,18);
895
896                        if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `ban_until` FROM `%s` WHERE `%s` = '%d'", login_db, login_db_account_id, account_id) )
897                                Sql_ShowDebug(sql_handle);
898                        else if( SQL_SUCCESS == Sql_NextRow(sql_handle) )
899                        {
900                                char* data;
901
902                                Sql_GetData(sql_handle, 0, &data, NULL);
903                                tmptime = (time_t)strtoul(data, NULL, 10);
904                                if( tmptime > time(NULL) )
905                                        timestamp = tmptime;
906                        }
907                        tmtime = localtime(&timestamp);
908                        tmtime->tm_year = tmtime->tm_year + year;
909                        tmtime->tm_mon  = tmtime->tm_mon  + month;
910                        tmtime->tm_mday = tmtime->tm_mday + mday;
911                        tmtime->tm_hour = tmtime->tm_hour + hour;
912                        tmtime->tm_min  = tmtime->tm_min  + min;
913                        tmtime->tm_sec  = tmtime->tm_sec  + sec;
914                        timestamp = mktime(tmtime);
915                        if( timestamp != (time_t)-1 )
916                        {
917                                if( timestamp <= time(NULL) )
918                                        timestamp = 0;
919                                if( tmptime != timestamp )
920                                {
921                                        if( timestamp != 0 )
922                                        {
923                                                uint8 buf[11];
924                                                WBUFW(buf,0) = 0x2731;
925                                                WBUFL(buf,2) = account_id;
926                                                WBUFB(buf,6) = 1; // 0: change of statut, 1: ban
927                                                WBUFL(buf,7) = (uint32)timestamp; // status or final date of a banishment
928                                                charif_sendallwos(-1, buf, 11);
929                                        }
930                                        ShowNotice("Account: %d Banned until: %lu\n", account_id, (unsigned long)timestamp);
931                                        if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `ban_until` = '%lu' WHERE `%s` = '%d'", login_db, (unsigned long)timestamp, login_db_account_id, account_id) )
932                                                Sql_ShowDebug(sql_handle);
933                                }
934                        }
935                }
936                break;
937
938                case 0x2727: // Change of sex (sex is reversed)
939                        if( RFIFOREST(fd) < 6 )
940                                return 0;
941                {
942                        int account_id = RFIFOL(fd,2);
943                        RFIFOSKIP(fd,6);
944
945                        if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `sex` FROM `%s` WHERE `%s` = '%d'", login_db, login_db_account_id, account_id) )
946                                Sql_ShowDebug(sql_handle);
947                        else if( SQL_SUCCESS == Sql_NextRow(sql_handle) )
948                        {
949                                unsigned char buf[7];
950                                int sex;
951                                char* data;
952
953                                Sql_GetData(sql_handle, 0, &data, NULL);
954                                sex = ( *data == 'M' ) ? 'F' : 'M'; //Change gender
955
956                                if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `sex` = '%c' WHERE `%s` = '%d'", login_db, sex, login_db_account_id, account_id) )
957                                        Sql_ShowDebug(sql_handle);
958
959                                WBUFW(buf,0) = 0x2723;
960                                WBUFL(buf,2) = account_id;
961                                WBUFB(buf,6) = sex_str2num(sex);
962                                charif_sendallwos(-1, buf, 7);
963                        }
964                }
965                break;
966
967                case 0x2728:    // save account_reg2
968                        if( RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2) )
969                                return 0;
970                        if( RFIFOL(fd,4) > 0 )
971                        {
972                                SqlStmt* stmt;
973                                int account_id;
974                                size_t off;
975
976                                account_id = RFIFOL(fd,4);
977
978                                //Delete all global account variables....
979                                if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `type`='1' AND `account_id`='%d';", reg_db, account_id) )
980                                        Sql_ShowDebug(sql_handle);
981
982                                //Proceed to insert them....
983                                stmt = SqlStmt_Malloc(sql_handle);
984                                if( SQL_ERROR == SqlStmt_Prepare(stmt, "INSERT INTO `%s` (`type`, `account_id`, `str`, `value`) VALUES ( 1 , '%d' , ? , ?);",  reg_db, account_id) )
985                                        SqlStmt_ShowDebug(stmt);
986                                for( i = 0, off = 13; i < ACCOUNT_REG2_NUM && off < RFIFOW(fd,2); ++i )
987                                {
988                                        char* p;
989                                        size_t len;
990
991                                        // str
992                                        p = (char*)RFIFOP(fd,off);
993                                        len = strlen(p);
994                                        SqlStmt_BindParam(stmt, 0, SQLDT_STRING, p, len);
995                                        off += len + 1;
996
997                                        // value
998                                        p = (char*)RFIFOP(fd,off);
999                                        len = strlen(p);
1000                                        SqlStmt_BindParam(stmt, 1, SQLDT_STRING, p, len);
1001                                        off += len + 1;
1002
1003                                        if( SQL_ERROR == SqlStmt_Execute(stmt) )
1004                                                SqlStmt_ShowDebug(stmt);
1005                                }
1006                                SqlStmt_Free(stmt);
1007
1008                                // Sending information towards the other char-servers.
1009                                RFIFOW(fd,0) = 0x2729;// reusing read buffer
1010                                charif_sendallwos(fd, RFIFOP(fd,0), RFIFOW(fd,2));
1011                                RFIFOSKIP(fd,RFIFOW(fd,2));
1012                        }
1013                break;
1014
1015                case 0x272a:    // Receiving of map-server via char-server an unban request
1016                        if( RFIFOREST(fd) < 6 )
1017                                return 0;
1018                {
1019                        int account_id = RFIFOL(fd,2);
1020                        RFIFOSKIP(fd,6);
1021
1022                        if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `ban_until` FROM `%s` WHERE `%s` = '%d'", login_db, login_db_account_id, account_id) )
1023                                Sql_ShowDebug(sql_handle);
1024                        else if( Sql_NumRows(sql_handle) > 0 )
1025                        {// Found a match
1026                                if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `ban_until` = '0' WHERE `%s` = '%d'", login_db, login_db_account_id, account_id) )
1027                                        Sql_ShowDebug(sql_handle);
1028                        }
1029                }
1030                break;
1031
1032                case 0x272b:    // Set account_id to online [Wizputer]
1033                        if( RFIFOREST(fd) < 6 )
1034                                return 0;
1035                        add_online_user(id, RFIFOL(fd,2));
1036                        RFIFOSKIP(fd,6);
1037                break;
1038
1039                case 0x272c:   // Set account_id to offline [Wizputer]
1040                        if( RFIFOREST(fd) < 6 )
1041                                return 0;
1042                        remove_online_user(RFIFOL(fd,2));
1043                        RFIFOSKIP(fd,6);
1044                break;
1045
1046                case 0x272d:    // Receive list of all online accounts. [Skotlex]
1047                        if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2))
1048                                return 0;
1049                        if( login_config.online_check )
1050                        {
1051                                struct online_login_data *p;
1052                                int aid;
1053                                uint32 i, users;
1054                                online_db->foreach(online_db, online_db_setoffline, id); //Set all chars from this char-server offline first
1055                                users = RFIFOW(fd,4);
1056                                for (i = 0; i < users; i++) {
1057                                        aid = RFIFOL(fd,6+i*4);
1058                                        p = (struct online_login_data*)idb_ensure(online_db, aid, create_online_user);
1059                                        p->char_server = id;
1060                                        if (p->waiting_disconnect != -1)
1061                                        {
1062                                                delete_timer(p->waiting_disconnect, waiting_disconnect_timer);
1063                                                p->waiting_disconnect = -1;
1064                                        }
1065                                }
1066                        }
1067                        RFIFOSKIP(fd,RFIFOW(fd,2));
1068                break;
1069
1070                case 0x272e: //Request account_reg2 for a character.
1071                        if (RFIFOREST(fd) < 10)
1072                                return 0;
1073                {
1074                        size_t off;
1075
1076                        int account_id = RFIFOL(fd,2);
1077                        int char_id = RFIFOL(fd,6);
1078                        RFIFOSKIP(fd,10);
1079
1080                        WFIFOHEAD(fd,10000);
1081                        WFIFOW(fd,0) = 0x2729;
1082                        WFIFOL(fd,4) = account_id;
1083                        WFIFOL(fd,8) = char_id;
1084                        WFIFOB(fd,12) = 1; //Type 1 for Account2 registry
1085
1086                        if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `str`,`value` FROM `%s` WHERE `type`='1' AND `account_id`='%d'", reg_db, account_id) )
1087                                Sql_ShowDebug(sql_handle);
1088
1089                        off = 13;
1090                        while( SQL_SUCCESS == Sql_NextRow(sql_handle) && off < 9000 )
1091                        {
1092                                char* data;
1093                               
1094                                // str
1095                                Sql_GetData(sql_handle, 0, &data, NULL);
1096                                if( *data != '\0' )
1097                                {
1098                                        off += sprintf((char*)WFIFOP(fd,off), "%s", data)+1; //We add 1 to consider the '\0' in place.
1099                                       
1100                                        // value
1101                                        Sql_GetData(sql_handle, 1, &data, NULL);
1102                                        off += sprintf((char*)WFIFOP(fd,off), "%s", data)+1;
1103                                }
1104                        }
1105                        Sql_FreeResult(sql_handle);
1106
1107                        if( off >= 9000 )
1108                                ShowWarning("Too many account2 registries for AID %d. Some registries were not sent.\n", account_id);
1109
1110                        WFIFOW(fd,2) = (uint16)off;
1111                        WFIFOSET(fd,WFIFOW(fd,2));
1112                }
1113                break;
1114
1115                case 0x2736: // WAN IP update from char-server
1116                        if( RFIFOREST(fd) < 6 )
1117                                return 0;
1118                        server[id].ip = ntohl(RFIFOL(fd,2));
1119                        ShowInfo("Updated IP of Server #%d to %d.%d.%d.%d.\n",id, CONVIP(server[id].ip));
1120                        RFIFOSKIP(fd,6);
1121                break;
1122
1123                case 0x2737: //Request to set all offline.
1124                        ShowInfo("Setting accounts from char-server %d offline.\n", id);
1125                        online_db->foreach(online_db, online_db_setoffline, id);
1126                        RFIFOSKIP(fd,2);
1127                break;
1128
1129                default:
1130                        ShowError("parse_fromchar: Unknown packet 0x%x from a char-server! Disconnecting!\n", command);
1131                        set_eof(fd);
1132                        return 0;
1133                } // switch
1134        } // while
1135
1136        RFIFOSKIP(fd,RFIFOREST(fd));
1137        return 0;
1138}
1139
1140//--------------------------------------------
1141// Test to know if an IP come from LAN or WAN.
1142//--------------------------------------------
1143int lan_subnetcheck(uint32 ip)
1144{
1145        int i;
1146        ARR_FIND( 0, subnet_count, i, (subnet[i].char_ip & subnet[i].mask) == (ip & subnet[i].mask) );
1147        return ( i < subnet_count ) ? subnet[i].char_ip : 0;
1148}
1149
1150int login_ip_ban_check(uint32 ip)
1151{
1152        uint8* p = (uint8*)&ip;
1153        char* data = NULL;
1154        int matches;
1155
1156        if( SQL_ERROR == Sql_Query(sql_handle, "SELECT count(*) FROM `ipbanlist` WHERE `list` = '%u.*.*.*' OR `list` = '%u.%u.*.*' OR `list` = '%u.%u.%u.*' OR `list` = '%u.%u.%u.%u'",
1157                p[3], p[3], p[2], p[3], p[2], p[1], p[3], p[2], p[1], p[0]) )
1158        {
1159                Sql_ShowDebug(sql_handle);
1160                // close connection because we can't verify their connectivity.
1161                return 1;
1162        }
1163
1164        if( SQL_ERROR == Sql_NextRow(sql_handle) )
1165                return 1;// Shouldn't happen, but just in case...
1166
1167        Sql_GetData(sql_handle, 0, &data, NULL);
1168        matches = atoi(data);
1169        Sql_FreeResult(sql_handle);
1170
1171        if( matches == 0 )
1172                return 0;// No ban
1173
1174        // ip ban ok.
1175        ShowInfo("Packet from banned ip : %u.%u.%u.%u\n", CONVIP(ip));
1176
1177        if( login_config.log_login && SQL_ERROR == Sql_Query(sql_handle, "INSERT DELAYED INTO `%s`(`time`,`ip`,`user`,`rcode`,`log`) VALUES (NOW(), '%u', 'unknown','-3', 'ip banned')", loginlog_db, ip) )
1178                Sql_ShowDebug(sql_handle);
1179        return 1;
1180}
1181
1182void login_auth_ok(struct login_session_data* sd)
1183{
1184        int fd = sd->fd;
1185        uint32 ip = session[fd]->client_addr;
1186
1187        char esc_userid[NAME_LENGTH*2+1];
1188        uint8 server_num, n;
1189        uint32 subnet_char_ip;
1190        struct auth_node* node;
1191        int i;
1192
1193        Sql_EscapeStringLen(sql_handle, esc_userid, sd->userid, strlen(sd->userid));
1194
1195        if( sd->level < login_config.min_level_to_connect )
1196        {
1197                ShowStatus("Connection refused: the minimum GM level for connection is %d (account: %s, GM level: %d).\n", login_config.min_level_to_connect, sd->userid, sd->level);
1198                WFIFOHEAD(fd,3);
1199                WFIFOW(fd,0) = 0x81;
1200                WFIFOB(fd,2) = 1; // 01 = Server closed
1201                WFIFOSET(fd,3);
1202                return;
1203        }
1204
1205        server_num = 0;
1206        for( i = 0; i < MAX_SERVERS; ++i )
1207                if( session_isValid(server[i].fd) )
1208                        server_num++;
1209
1210        if( server_num == 0 )
1211        {// if no char-server, don't send void list of servers, just disconnect the player with proper message
1212                ShowStatus("Connection refused: there is no char-server online (account: %s).\n", sd->userid);
1213                WFIFOHEAD(fd,3);
1214                WFIFOW(fd,0) = 0x81;
1215                WFIFOB(fd,2) = 1; // 01 = Server closed
1216                WFIFOSET(fd,3);
1217                return;
1218        }
1219
1220        if( login_config.online_check )
1221        {
1222                struct online_login_data* data = (struct online_login_data*)idb_get(online_db, sd->account_id);
1223                if( data )
1224                {// account is already marked as online!
1225                        if( data->char_server > -1 )
1226                        {// Request char servers to kick this account out. [Skotlex]
1227                                uint8 buf[6];
1228                                ShowNotice("User '%s' is already online - Rejected.\n", sd->userid);
1229                                WBUFW(buf,0) = 0x2734;
1230                                WBUFL(buf,2) = sd->account_id;
1231                                charif_sendallwos(-1, buf, 6);
1232                                if( data->waiting_disconnect == -1 )
1233                                        data->waiting_disconnect = add_timer(gettick()+AUTH_TIMEOUT, waiting_disconnect_timer, sd->account_id, 0);
1234
1235                                WFIFOHEAD(fd,3);
1236                                WFIFOW(fd,0) = 0x81;
1237                                WFIFOB(fd,2) = 8; // 08 = Server still recognizes your last login
1238                                WFIFOSET(fd,3);
1239                                return;
1240                        }
1241                        else
1242                        if( data->char_server == -1 )
1243                        {// client has authed but did not access char-server yet
1244                                // wipe previous session
1245                                idb_remove(auth_db, sd->account_id);
1246                                remove_online_user(sd->account_id);
1247                                data = NULL;
1248                        }
1249                }
1250        }
1251
1252        if( login_config.log_login && SQL_ERROR == Sql_Query(sql_handle, "INSERT DELAYED INTO `%s`(`time`,`ip`,`user`,`rcode`,`log`) VALUES (NOW(), '%u', '%s','100', 'login ok')", loginlog_db, ip, esc_userid) )
1253                Sql_ShowDebug(sql_handle);
1254
1255        if( sd->level > 0 )
1256                ShowStatus("Connection of the GM (level:%d) account '%s' accepted.\n", sd->level, sd->userid);
1257        else
1258                ShowStatus("Connection of the account '%s' accepted.\n", sd->userid);
1259
1260        WFIFOHEAD(fd,47+32*server_num);
1261        WFIFOW(fd,0) = 0x69;
1262        WFIFOW(fd,2) = 47+32*server_num;
1263        WFIFOL(fd,4) = sd->login_id1;
1264        WFIFOL(fd,8) = sd->account_id;
1265        WFIFOL(fd,12) = sd->login_id2;
1266        WFIFOL(fd,16) = 0; // in old version, that was for ip (not more used)
1267        //memcpy(WFIFOP(fd,20), sd->lastlogin, 24); // in old version, that was for name (not more used)
1268        WFIFOW(fd,44) = 0; // unknown
1269        WFIFOB(fd,46) = sex_str2num(sd->sex);
1270        for( i = 0, n = 0; i < MAX_SERVERS; ++i )
1271        {
1272                if( !session_isValid(server[i].fd) )
1273                        continue;
1274
1275                subnet_char_ip = lan_subnetcheck(ip); // Advanced subnet check [LuzZza]
1276                WFIFOL(fd,47+n*32) = htonl((subnet_char_ip) ? subnet_char_ip : server[i].ip);
1277                WFIFOW(fd,47+n*32+4) = ntows(htons(server[i].port)); // [!] LE byte order here [!]
1278                memcpy(WFIFOP(fd,47+n*32+6), server[i].name, 20);
1279                WFIFOW(fd,47+n*32+26) = server[i].users;
1280                WFIFOW(fd,47+n*32+28) = server[i].maintenance;
1281                WFIFOW(fd,47+n*32+30) = server[i].new_;
1282                n++;
1283        }
1284        WFIFOSET(fd,47+32*server_num);
1285
1286        // create temporary auth entry
1287        CREATE(node, struct auth_node, 1);
1288        node->account_id = sd->account_id;
1289        node->login_id1 = sd->login_id1;
1290        node->login_id2 = sd->login_id2;
1291        node->sex = sd->sex;
1292        node->ip = ip;
1293        idb_put(auth_db, sd->account_id, node);
1294
1295        if( login_config.online_check )
1296        {
1297                struct online_login_data* data;
1298
1299                // mark client as 'online'
1300                data = add_online_user(-1, sd->account_id);
1301
1302                // schedule deletion of this node
1303                data->waiting_disconnect = add_timer(gettick()+AUTH_TIMEOUT, waiting_disconnect_timer, sd->account_id, 0);
1304        }
1305}
1306
1307void login_auth_failed(struct login_session_data* sd, int result)
1308{
1309        int fd = sd->fd;
1310        uint32 ip = session[fd]->client_addr;
1311        char esc_userid[NAME_LENGTH*2+1];
1312
1313        Sql_EscapeStringLen(sql_handle, esc_userid, sd->userid, strlen(sd->userid));
1314
1315        if (login_config.log_login)
1316        {
1317                const char* error;
1318                switch( result ) {
1319                case   0: error = "Unregistered ID."; break; // 0 = Unregistered ID
1320                case   1: error = "Incorrect Password."; break; // 1 = Incorrect Password
1321                case   2: error = "Account Expired."; break; // 2 = This ID is expired
1322                case   3: error = "Rejected from server."; break; // 3 = Rejected from Server
1323                case   4: error = "Blocked by GM."; break; // 4 = You have been blocked by the GM Team
1324                case   5: error = "Not latest game EXE."; break; // 5 = Your Game's EXE file is not the latest version
1325                case   6: error = "Banned."; break; // 6 = Your are Prohibited to log in until %s
1326                case   7: error = "Server Over-population."; break; // 7 = Server is jammed due to over populated
1327                case   8: error = "Account limit from company"; break; // 8 = No more accounts may be connected from this company
1328                case   9: error = "Ban by DBA"; break; // 9 = MSI_REFUSE_BAN_BY_DBA
1329                case  10: error = "Email not confirmed"; break; // 10 = MSI_REFUSE_EMAIL_NOT_CONFIRMED
1330                case  11: error = "Ban by GM"; break; // 11 = MSI_REFUSE_BAN_BY_GM
1331                case  12: error = "Working in DB"; break; // 12 = MSI_REFUSE_TEMP_BAN_FOR_DBWORK
1332                case  13: error = "Self Lock"; break; // 13 = MSI_REFUSE_SELF_LOCK
1333                case  14: error = "Not Permitted Group"; break; // 14 = MSI_REFUSE_NOT_PERMITTED_GROUP
1334                case  15: error = "Not Permitted Group"; break; // 15 = MSI_REFUSE_NOT_PERMITTED_GROUP
1335                case  99: error = "Account gone."; break; // 99 = This ID has been totally erased
1336                case 100: error = "Login info remains."; break; // 100 = Login information remains at %s
1337                case 101: error = "Hacking investigation."; break; // 101 = Account has been locked for a hacking investigation. Please contact the GM Team for more information
1338                case 102: error = "Bug investigation."; break; // 102 = This account has been temporarily prohibited from login due to a bug-related investigation
1339                case 103: error = "Deleting char."; break; // 103 = This character is being deleted. Login is temporarily unavailable for the time being
1340                case 104: error = "Deleting spouse char."; break; // 104 = This character is being deleted. Login is temporarily unavailable for the time being
1341                default : error = "Unknown Error."; break;
1342                }
1343
1344                if( SQL_ERROR == Sql_Query(sql_handle, "INSERT DELAYED INTO `%s`(`time`,`ip`,`user`,`rcode`,`log`) VALUES (NOW(), '%u', '%s', '%d','login failed : %s')", loginlog_db, ip, esc_userid, result, error) )
1345                        Sql_ShowDebug(sql_handle);
1346        }
1347
1348        if( result == 1 && login_config.dynamic_pass_failure_ban && login_config.log_login ) // failed password
1349        {
1350                unsigned long failures = 0;
1351                if( SQL_ERROR == Sql_Query(sql_handle, "SELECT count(*) FROM `%s` WHERE `ip` = '%u' AND `rcode` = '1' AND `time` > NOW() - INTERVAL %d MINUTE",
1352                        loginlog_db, ip, login_config.dynamic_pass_failure_ban_interval) )// how many times failed account? in one ip.
1353                        Sql_ShowDebug(sql_handle);
1354
1355                //check query result
1356                if( SQL_SUCCESS == Sql_NextRow(sql_handle) )
1357                {
1358                        char* data;
1359                        Sql_GetData(sql_handle, 0, &data, NULL);
1360                        failures = strtoul(data, NULL, 10);
1361                        Sql_FreeResult(sql_handle);
1362                }
1363                if( failures >= login_config.dynamic_pass_failure_ban_limit )
1364                {
1365                        uint8* p = (uint8*)&ip;
1366                        if( SQL_ERROR == Sql_Query(sql_handle, "INSERT INTO `ipbanlist`(`list`,`btime`,`rtime`,`reason`) VALUES ('%u.%u.%u.*', NOW() , NOW() +  INTERVAL %d MINUTE ,'Password error ban: %s')", p[3], p[2], p[1], login_config.dynamic_pass_failure_ban_duration, esc_userid) )
1367                                Sql_ShowDebug(sql_handle);
1368                }
1369        }
1370
1371        WFIFOHEAD(fd,23);
1372        WFIFOW(fd,0) = 0x6a;
1373        WFIFOB(fd,2) = (uint8)result;
1374        if( result != 6 )
1375                memset(WFIFOP(fd,3), '\0', 20);
1376        else
1377        {// 6 = Your are Prohibited to log in until %s
1378                if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `ban_until` FROM `%s` WHERE `%s` = %s '%s'", login_db, login_db_userid, (login_config.case_sensitive ? "BINARY" : ""), esc_userid) )
1379                        Sql_ShowDebug(sql_handle);
1380                else if( SQL_SUCCESS == Sql_NextRow(sql_handle) )
1381                {
1382                        char* data;
1383                        time_t unban_time;
1384
1385                        Sql_GetData(sql_handle, 0, &data, NULL);
1386                        unban_time = (time_t)strtoul(data, NULL, 10);
1387                        Sql_FreeResult(sql_handle);
1388
1389                        strftime((char*)WFIFOP(fd,3), 20, login_config.date_format, localtime(&unban_time));
1390                }
1391        }
1392        WFIFOSET(fd,23);
1393}
1394
1395//----------------------------------------------------------------------------------------
1396// Default packet parsing (normal players or administation/char-server connection requests)
1397//----------------------------------------------------------------------------------------
1398int parse_login(int fd)
1399{
1400        struct login_session_data* sd = session[fd]->session_data;
1401        int result;
1402        uint32 ipl;
1403        char ip[16];
1404
1405        if( session[fd]->flag.eof )
1406        {
1407                do_close(fd);
1408                return 0;
1409        }
1410
1411        if( sd == NULL ) {
1412                sd = CREATE(session[fd]->session_data, struct login_session_data, 1);
1413                sd->fd = fd;
1414        }               
1415
1416        ipl = session[fd]->client_addr;
1417        ip2str(ipl, ip);
1418
1419        while( RFIFOREST(fd) >= 2 )
1420        {
1421                uint16 command = RFIFOW(fd,0);
1422
1423                switch( command )
1424                {
1425
1426                case 0x0200:            // New alive packet: structure: 0x200 <account.userid>.24B. used to verify if client is always alive.
1427                        if (RFIFOREST(fd) < 26)
1428                                return 0;
1429                        RFIFOSKIP(fd,26);
1430                break;
1431
1432                case 0x0204:            // New alive packet: structure: 0x204 <encrypted.account.userid>.16B. (new ragexe from 22 june 2004)
1433                        if (RFIFOREST(fd) < 18)
1434                                return 0;
1435                        RFIFOSKIP(fd,18);
1436                break;
1437
1438                case 0x0064:            // request client login
1439                case 0x01dd:            // request client login (encryption mode)
1440                case 0x0277:            // New login packet (kRO 2006-04-24aSakexe langtype 0)
1441                case 0x02b0:            // New login packet (kRO 2007-05-14aSakexe langtype 0)
1442                {
1443                        size_t packet_len = RFIFOREST(fd); // assume no other packet was sent
1444
1445                        // Perform ip-ban check
1446                        if( login_config.ipban && login_ip_ban_check(ipl) )
1447                        {
1448                                ShowStatus("Connection refused: IP isn't authorised (deny/allow, ip: %s).\n", ip);
1449                                WFIFOHEAD(fd,23);
1450                                WFIFOW(fd,0) = 0x6a;
1451                                WFIFOB(fd,2) = 3; // 3 = Rejected from Server
1452                                WFIFOSET(fd,23);
1453                                RFIFOSKIP(fd,packet_len);
1454                                set_eof(fd);
1455                                break;
1456                        }
1457
1458                        if( (command == 0x0064 && packet_len < 55)
1459                        ||  (command == 0x01dd && packet_len < 47)
1460                        ||  (command == 0x0277 && packet_len < 84)
1461                        ||  (command == 0x02b0 && packet_len < 85) )
1462                                return 0;
1463
1464                        // S 0064 <version>.l <account name>.24B <password>.24B <version2>.B
1465                        // S 01dd <version>.l <account name>.24B <md5 binary>.16B <version2>.B
1466                        // S 0277 <version>.l <account name>.24B <password>.24B <junk?>.29B <version2>.B
1467                        // S 02b0 <version>.l <account name>.24B <password>.24B <junk?>.30B <version2>.B
1468
1469                        sd->version = RFIFOL(fd,2);
1470                        safestrncpy(sd->userid, (char*)RFIFOP(fd,6), NAME_LENGTH);//## does it have to be nul-terminated?
1471                        if (command != 0x01dd) {
1472                                ShowStatus("Request for connection of %s (ip: %s).\n", sd->userid, ip);
1473                                safestrncpy(sd->passwd, (char*)RFIFOP(fd,30), NAME_LENGTH);//## does it have to be nul-terminated?
1474                                sd->passwdenc = 0;
1475                        } else {
1476                                ShowStatus("Request for connection (encryption mode) of %s (ip: %s).\n", sd->userid, ip);
1477                                memcpy(sd->passwd, RFIFOP(fd,30), 16); sd->passwd[16] = '\0'; // raw binary data here!
1478                                sd->passwdenc = PASSWORDENC;
1479                        }
1480                        RFIFOSKIP(fd,packet_len);
1481
1482                        result = mmo_auth(sd);
1483
1484                        if( result == -1 )
1485                                login_auth_ok(sd);
1486                        else
1487                                login_auth_failed(sd, result);
1488                }
1489                break;
1490
1491                case 0x01db:    // Sending request of the coding key
1492                        RFIFOSKIP(fd,2);
1493                {
1494                        unsigned int i;
1495
1496                        memset(sd->md5key, '\0', sizeof(sd->md5key));
1497                        sd->md5keylen = (uint16)(12 + rand() % 4);
1498                        for( i = 0; i < sd->md5keylen; ++i )
1499                                sd->md5key[i] = (char)(1 + rand() % 255);
1500
1501                        WFIFOHEAD(fd,4 + sd->md5keylen);
1502                        WFIFOW(fd,0) = 0x01dc;
1503                        WFIFOW(fd,2) = 4 + sd->md5keylen;
1504                        memcpy(WFIFOP(fd,4), sd->md5key, sd->md5keylen);
1505                        WFIFOSET(fd,WFIFOW(fd,2));
1506                }
1507                break;
1508
1509                case 0x2710:    // Connection request of a char-server
1510                        if (RFIFOREST(fd) < 86)
1511                                return 0;
1512                {
1513                        char esc_userid[NAME_LENGTH*2+1];
1514                        char server_name[20];
1515                        char esc_server_name[20*2+1];
1516                        uint32 server_ip;
1517                        uint16 server_port;
1518                        uint16 maintenance;
1519                        uint16 new_;
1520
1521                        safestrncpy(sd->userid, (char*)RFIFOP(fd,2), NAME_LENGTH);
1522                        safestrncpy(sd->passwd, (char*)RFIFOP(fd,26), NAME_LENGTH);
1523                        sd->passwdenc = 0;
1524                        sd->version = login_config.client_version_to_connect; // hack to skip version check
1525                        server_ip = ntohl(RFIFOL(fd,54));
1526                        server_port = ntohs(RFIFOW(fd,58));
1527                        safestrncpy(server_name, (char*)RFIFOP(fd,60), 20);
1528                        maintenance = RFIFOW(fd,82);
1529                        new_ = RFIFOW(fd,84);
1530                        RFIFOSKIP(fd,86);
1531
1532                        Sql_EscapeStringLen(sql_handle, esc_server_name, server_name, strnlen(server_name, 20));
1533                        Sql_EscapeStringLen(sql_handle, esc_userid, sd->userid, strnlen(sd->userid, NAME_LENGTH));
1534
1535                        ShowInfo("Connection request of the char-server '%s' @ %d.%d.%d.%d:%d (account: '%s', pass: '%s', ip: '%s')\n", server_name, CONVIP(server_ip), server_port, sd->userid, sd->passwd, ip);
1536
1537                        if( login_config.log_login && SQL_ERROR == Sql_Query(sql_handle, "INSERT DELAYED INTO `%s`(`time`,`ip`,`user`,`rcode`,`log`) VALUES (NOW(), '%u', '%s@%s','100', 'charserver - %s@%u.%u.%u.%u:%d')",
1538                                loginlog_db, ipl, esc_userid, esc_server_name, esc_server_name, CONVIP(server_ip), server_port) )
1539                                Sql_ShowDebug(sql_handle);
1540
1541                        result = mmo_auth(sd);
1542                        if( result == -1 && sd->sex == 'S' && sd->account_id < MAX_SERVERS && server[sd->account_id].fd == -1 )
1543                        {
1544                                ShowStatus("Connection of the char-server '%s' accepted.\n", server_name);
1545                                safestrncpy(server[sd->account_id].name, server_name, sizeof(server[sd->account_id].name));
1546                                server[sd->account_id].fd = fd;
1547                                server[sd->account_id].ip = server_ip;
1548                                server[sd->account_id].port = server_port;
1549                                server[sd->account_id].users = 0;
1550                                server[sd->account_id].maintenance = maintenance;
1551                                server[sd->account_id].new_ = new_;
1552
1553                                session[fd]->func_parse = parse_fromchar;
1554                                session[fd]->flag.server = 1;
1555                                realloc_fifo(fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK);
1556
1557                                // send connection success
1558                                WFIFOHEAD(fd,3);
1559                                WFIFOW(fd,0) = 0x2711;
1560                                WFIFOB(fd,2) = 0;
1561                                WFIFOSET(fd,3);
1562
1563                                // send GM account to char-server
1564                                send_GM_accounts(fd);
1565
1566                                if( SQL_ERROR == Sql_Query(sql_handle, "REPLACE INTO `sstatus`(`index`,`name`,`user`) VALUES ( '%d', '%s', '%d')", sd->account_id, esc_server_name, 0) )
1567                                        Sql_ShowDebug(sql_handle);
1568                        }
1569                        else
1570                        {
1571                                ShowNotice("Connection of the char-server '%s' REFUSED.\n", server_name);
1572                                WFIFOHEAD(fd,3);
1573                                WFIFOW(fd,0) = 0x2711;
1574                                WFIFOB(fd,2) = 3;
1575                                WFIFOSET(fd,3);
1576                        }
1577                }
1578                return 0; // processing will continue elsewhere
1579
1580                case 0x7530:    // Server version information request
1581                        ShowStatus("Sending server version information to ip: %s\n", ip);
1582                        RFIFOSKIP(fd,2);
1583                        WFIFOHEAD(fd,10);
1584                        WFIFOW(fd,0) = 0x7531;
1585                        WFIFOB(fd,2) = ATHENA_MAJOR_VERSION;
1586                        WFIFOB(fd,3) = ATHENA_MINOR_VERSION;
1587                        WFIFOB(fd,4) = ATHENA_REVISION;
1588                        WFIFOB(fd,5) = ATHENA_RELEASE_FLAG;
1589                        WFIFOB(fd,6) = ATHENA_OFFICIAL_FLAG;
1590                        WFIFOB(fd,7) = ATHENA_SERVER_LOGIN;
1591                        WFIFOW(fd,8) = ATHENA_MOD_VERSION;
1592                        WFIFOSET(fd,10);
1593                break;
1594
1595                default:
1596                        ShowNotice("Abnormal end of connection (ip: %s): Unknown packet 0x%x\n", ip, command);
1597                        set_eof(fd);
1598                        return 0;
1599                }
1600        }
1601
1602        RFIFOSKIP(fd,RFIFOREST(fd));
1603        return 0;
1604}
1605
1606//-----------------------
1607// Console Command Parser [Wizputer]
1608//-----------------------
1609int parse_console(char* buf)
1610{
1611        char command[256];
1612
1613        memset(command, 0, sizeof(command));
1614
1615        sscanf(buf, "%[^\n]", command);
1616
1617        ShowInfo("Console command :%s", command);
1618
1619        if( strcmpi("shutdown", command) == 0 ||
1620            strcmpi("exit", command) == 0 ||
1621            strcmpi("quit", command) == 0 ||
1622            strcmpi("end", command) == 0 )
1623                runflag = 0;
1624        else
1625        if( strcmpi("alive", command) == 0 ||
1626            strcmpi("status", command) == 0 )
1627                ShowInfo(CL_CYAN"Console: "CL_BOLD"I'm Alive."CL_RESET"\n");
1628        else
1629        if( strcmpi("help", command) == 0 ) {
1630                ShowInfo(CL_BOLD"Help of commands:"CL_RESET"\n");
1631                ShowInfo("  To shutdown the server:\n");
1632                ShowInfo("  'shutdown|exit|quit|end'\n");
1633                ShowInfo("  To know if server is alive:\n");
1634                ShowInfo("  'alive|status'\n");
1635        }
1636
1637        return 0;
1638}
1639
1640static int online_data_cleanup_sub(DBKey key, void *data, va_list ap)
1641{
1642        struct online_login_data *character= (struct online_login_data*)data;
1643        if (character->char_server == -2) //Unknown server.. set them offline
1644                remove_online_user(character->account_id);
1645        return 0;
1646}
1647
1648static int online_data_cleanup(int tid, unsigned int tick, int id, intptr data)
1649{
1650        online_db->foreach(online_db, online_data_cleanup_sub);
1651        return 0;
1652} 
1653
1654//----------------------------------
1655// Reading Lan Support configuration
1656//----------------------------------
1657int login_lan_config_read(const char *lancfgName)
1658{
1659        FILE *fp;
1660        int line_num = 0;
1661        char line[1024], w1[64], w2[64], w3[64], w4[64];
1662
1663        if((fp = fopen(lancfgName, "r")) == NULL) {
1664                ShowWarning("LAN Support configuration file is not found: %s\n", lancfgName);
1665                return 1;
1666        }
1667
1668        ShowInfo("Reading the configuration file %s...\n", lancfgName);
1669
1670        while(fgets(line, sizeof(line), fp))
1671        {
1672                line_num++;
1673                if ((line[0] == '/' && line[1] == '/') || line[0] == '\n' || line[1] == '\n')
1674                        continue;
1675
1676                if(sscanf(line,"%[^:]: %[^:]:%[^:]:%[^\r\n]", w1, w2, w3, w4) != 4)
1677                {
1678                        ShowWarning("Error syntax of configuration file %s in line %d.\n", lancfgName, line_num);
1679                        continue;
1680                }
1681
1682                if( strcmpi(w1, "subnet") == 0 )
1683                {
1684                        subnet[subnet_count].mask = str2ip(w2);
1685                        subnet[subnet_count].char_ip = str2ip(w3);
1686                        subnet[subnet_count].map_ip = str2ip(w4);
1687
1688                        if( (subnet[subnet_count].char_ip & subnet[subnet_count].mask) != (subnet[subnet_count].map_ip & subnet[subnet_count].mask) )
1689                        {
1690                                ShowError("%s: Configuration Error: The char server (%s) and map server (%s) belong to different subnetworks!\n", lancfgName, w3, w4);
1691                                continue;
1692                        }
1693
1694                        subnet_count++;
1695                }
1696        }
1697
1698        ShowStatus("Read information about %d subnetworks.\n", subnet_count);
1699
1700        fclose(fp);
1701        return 0;
1702}
1703
1704//-----------------------------------------------------
1705// clear expired ip bans
1706//-----------------------------------------------------
1707int ip_ban_flush(int tid, unsigned int tick, int id, intptr data)
1708{
1709        if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `ipbanlist` WHERE `rtime` <= NOW()") )
1710                Sql_ShowDebug(sql_handle);
1711
1712        return 0;
1713}
1714
1715//-----------------------------------
1716// Reading main configuration file
1717//-----------------------------------
1718int login_config_read(const char* cfgName)
1719{
1720        char line[1024], w1[1024], w2[1024];
1721        FILE* fp = fopen(cfgName, "r");
1722        if (fp == NULL) {
1723                ShowError("Configuration file (%s) not found.\n", cfgName);
1724                return 1;
1725        }
1726        ShowInfo("Reading configuration file %s...\n", cfgName);
1727        while(fgets(line, sizeof(line), fp))
1728        {
1729                if (line[0] == '/' && line[1] == '/')
1730                        continue;
1731
1732                if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) < 2)
1733                        continue;
1734
1735                if(!strcmpi(w1,"timestamp_format"))
1736                        strncpy(timestamp_format, w2, 20);
1737                else if(!strcmpi(w1,"stdout_with_ansisequence"))
1738                        stdout_with_ansisequence = config_switch(w2);
1739                else if(!strcmpi(w1,"console_silent")) {
1740                        ShowInfo("Console Silent Setting: %d\n", atoi(w2));
1741                        msg_silent = atoi(w2);
1742                }
1743                else if( !strcmpi(w1, "bind_ip") ) {
1744                        char ip_str[16];
1745                        login_config.login_ip = host2ip(w2);
1746                        if( login_config.login_ip )
1747                                ShowStatus("Login server binding IP address : %s -> %s\n", w2, ip2str(login_config.login_ip, ip_str));
1748                }
1749                else if( !strcmpi(w1, "login_port") ) {
1750                        login_config.login_port = (uint16)atoi(w2);
1751                        ShowStatus("set login_port : %s\n",w2);
1752                }
1753                else if(!strcmpi(w1, "log_login"))
1754                        login_config.log_login = (bool)config_switch(w2);
1755
1756                else if(!strcmpi(w1, "ipban"))
1757                        login_config.ipban = (bool)config_switch(w2);
1758                else if(!strcmpi(w1, "dynamic_pass_failure_ban"))
1759                        login_config.dynamic_pass_failure_ban = (bool)config_switch(w2);
1760                else if(!strcmpi(w1, "dynamic_pass_failure_ban_interval"))
1761                        login_config.dynamic_pass_failure_ban_interval = atoi(w2);
1762                else if(!strcmpi(w1, "dynamic_pass_failure_ban_limit"))
1763                        login_config.dynamic_pass_failure_ban_limit = atoi(w2);
1764                else if(!strcmpi(w1, "dynamic_pass_failure_ban_duration"))
1765                        login_config.dynamic_pass_failure_ban_duration = atoi(w2);
1766
1767                else if(!strcmpi(w1, "new_account"))
1768                        login_config.new_account_flag = (bool)config_switch(w2);
1769                else if(!strcmpi(w1, "start_limited_time"))
1770                        login_config.start_limited_time = atoi(w2);
1771                else if(!strcmpi(w1, "check_client_version"))
1772                        login_config.check_client_version = (bool)config_switch(w2);
1773                else if(!strcmpi(w1, "client_version_to_connect"))
1774                        login_config.client_version_to_connect = atoi(w2);
1775                else if(!strcmpi(w1, "use_MD5_passwords"))
1776                        login_config.use_md5_passwds = (bool)config_switch(w2);
1777                else if(!strcmpi(w1, "min_level_to_connect"))
1778                        login_config.min_level_to_connect = atoi(w2);
1779                else if(!strcmpi(w1, "date_format"))
1780                        safestrncpy(login_config.date_format, w2, sizeof(login_config.date_format));
1781                else if(!strcmpi(w1, "console"))
1782                        login_config.console = (bool)config_switch(w2);
1783                else if(!strcmpi(w1, "case_sensitive"))
1784                        login_config.case_sensitive = (bool)config_switch(w2);
1785                else if(!strcmpi(w1, "allowed_regs")) //account flood protection system
1786                        allowed_regs = atoi(w2);
1787                else if(!strcmpi(w1, "time_allowed"))
1788                        time_allowed = atoi(w2);
1789                else if(!strcmpi(w1, "online_check"))
1790                        login_config.online_check = (bool)config_switch(w2);
1791                else if(!strcmpi(w1, "use_dnsbl"))
1792                        login_config.use_dnsbl = (bool)config_switch(w2);
1793                else if(!strcmpi(w1, "dnsbl_servers"))
1794                        safestrncpy(login_config.dnsbl_servs, w2, sizeof(login_config.dnsbl_servs));
1795                else if(!strcmpi(w1, "ip_sync_interval"))
1796                        login_config.ip_sync_interval = (unsigned int)1000*60*atoi(w2); //w2 comes in minutes.
1797                else if(!strcmpi(w1, "import"))
1798                        login_config_read(w2);
1799        }
1800        fclose(fp);
1801        ShowInfo("Finished reading %s.\n", cfgName);
1802        return 0;
1803}
1804
1805void sql_config_read(const char* cfgName)
1806{
1807        char line[1024], w1[1024], w2[1024];
1808        FILE* fp = fopen(cfgName, "r");
1809        if(fp == NULL) {
1810                ShowError("file not found: %s\n", cfgName);
1811                return;
1812        }
1813        ShowInfo("reading configuration file %s...\n", cfgName);
1814        while(fgets(line, sizeof(line), fp))
1815        {
1816                if (line[0] == '/' && line[1] == '/')
1817                        continue;
1818                if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) < 2)
1819                        continue;
1820
1821                if (!strcmpi(w1, "gm_read_method"))
1822                        login_config.login_gm_read = (atoi(w2) == 0);
1823                else if (!strcmpi(w1, "login_db"))
1824                        strcpy(login_db, w2);
1825                else if (!strcmpi(w1, "login_server_ip"))
1826                        strcpy(login_server_ip, w2);
1827                else if (!strcmpi(w1, "login_server_port"))
1828                        login_server_port = (uint16)atoi(w2);
1829                else if (!strcmpi(w1, "login_server_id"))
1830                        strcpy(login_server_id, w2);
1831                else if (!strcmpi(w1, "login_server_pw"))
1832                        strcpy(login_server_pw, w2);
1833                else if (!strcmpi(w1, "login_server_db"))
1834                        strcpy(login_server_db, w2);
1835                else if (!strcmpi(w1, "default_codepage"))
1836                        strcpy(default_codepage, w2);
1837                else if (!strcmpi(w1, "login_db_account_id"))
1838                        strcpy(login_db_account_id, w2);
1839                else if (!strcmpi(w1, "login_db_userid"))
1840                        strcpy(login_db_userid, w2);
1841                else if (!strcmpi(w1, "login_db_user_pass"))
1842                        strcpy(login_db_user_pass, w2);
1843                else if (!strcmpi(w1, "login_db_level"))
1844                        strcpy(login_db_level, w2);
1845                else if (!strcmpi(w1, "loginlog_db"))
1846                        strcpy(loginlog_db, w2);
1847                else if (!strcmpi(w1, "reg_db"))
1848                        strcpy(reg_db, w2);
1849                else if (!strcmpi(w1, "import"))
1850                        sql_config_read(w2);
1851        }
1852        fclose(fp);
1853        ShowInfo("Done reading %s.\n", cfgName);
1854}
1855
1856void login_set_defaults()
1857{
1858        login_config.login_ip = INADDR_ANY;
1859        login_config.login_port = 6900;
1860        login_config.ip_sync_interval = 0;
1861        login_config.log_login = true;
1862        safestrncpy(login_config.date_format, "%Y-%m-%d %H:%M:%S", sizeof(login_config.date_format));
1863        login_config.console = false;
1864        login_config.new_account_flag = true;
1865        login_config.case_sensitive = true;
1866        login_config.use_md5_passwds = false;
1867        login_config.login_gm_read = true;
1868        login_config.min_level_to_connect = 0;
1869        login_config.online_check = true;
1870        login_config.check_client_version = false;
1871        login_config.client_version_to_connect = 20;
1872
1873        login_config.ipban = true;
1874        login_config.dynamic_pass_failure_ban = true;
1875        login_config.dynamic_pass_failure_ban_interval = 5;
1876        login_config.dynamic_pass_failure_ban_limit = 7;
1877        login_config.dynamic_pass_failure_ban_duration = 5;
1878        login_config.use_dnsbl = false;
1879        safestrncpy(login_config.dnsbl_servs, "", sizeof(login_config.dnsbl_servs));
1880}
1881
1882//--------------------------------------
1883// Function called at exit of the server
1884//--------------------------------------
1885void do_final(void)
1886{
1887        int i, fd;
1888        ShowStatus("Terminating...\n");
1889
1890        mmo_db_close();
1891        online_db->destroy(online_db, NULL);
1892        auth_db->destroy(auth_db, NULL);
1893
1894        if(gm_account_db) aFree(gm_account_db);
1895
1896        for (i = 0; i < MAX_SERVERS; i++) {
1897                if ((fd = server[i].fd) >= 0) {
1898                        memset(&server[i], 0, sizeof(struct mmo_char_server));
1899                        server[i].fd = -1;
1900                        do_close(fd);
1901                }
1902        }
1903        do_close(login_fd);
1904
1905        ShowStatus("Finished.\n");
1906}
1907
1908//------------------------------
1909// Function called when the server
1910// has received a crash signal.
1911//------------------------------
1912void do_abort(void)
1913{
1914}
1915
1916void set_server_type(void)
1917{
1918        SERVER_TYPE = ATHENA_SERVER_LOGIN;
1919}
1920
1921//------------------------------
1922// Login server initialization
1923//------------------------------
1924int do_init(int argc, char** argv)
1925{
1926        int i;
1927
1928        login_set_defaults();
1929
1930        // read login-server configuration
1931        login_config_read((argc > 1) ? argv[1] : LOGIN_CONF_NAME);
1932        sql_config_read(SQL_CONF_NAME);
1933        login_lan_config_read((argc > 2) ? argv[2] : LAN_CONF_NAME);
1934
1935        srand((unsigned int)time(NULL));
1936
1937        for( i = 0; i < MAX_SERVERS; i++ )
1938                server[i].fd = -1;
1939
1940        // Online user database init
1941        online_db = idb_alloc(DB_OPT_RELEASE_DATA);
1942        add_timer_func_list(waiting_disconnect_timer, "waiting_disconnect_timer");
1943
1944        // Auth init
1945        auth_db = idb_alloc(DB_OPT_RELEASE_DATA);
1946        mmo_auth_sqldb_init();
1947
1948        // Read account information.
1949        if(login_config.login_gm_read)
1950                read_gm_account();
1951
1952        // set default parser as parse_login function
1953        set_defaultparse(parse_login);
1954
1955        // ban deleter timer
1956        add_timer_func_list(ip_ban_flush, "ip_ban_flush");
1957        add_timer_interval(gettick()+10, ip_ban_flush, 0, 0, 60*1000);
1958
1959        // every 10 minutes cleanup online account db.
1960        add_timer_func_list(online_data_cleanup, "online_data_cleanup");
1961        add_timer_interval(gettick() + 600*1000, online_data_cleanup, 0, 0, 600*1000);
1962
1963        // add timer to detect ip address change and perform update
1964        if (login_config.ip_sync_interval) {
1965                add_timer_func_list(sync_ip_addresses, "sync_ip_addresses");
1966                add_timer_interval(gettick() + login_config.ip_sync_interval, sync_ip_addresses, 0, 0, login_config.ip_sync_interval);
1967        }
1968
1969        if( login_config.console )
1970        {
1971                //##TODO invoke a CONSOLE_START plugin event
1972        }
1973
1974        new_reg_tick = gettick();
1975
1976        // server port open & binding
1977        login_fd = make_listen_bind(login_config.login_ip, login_config.login_port);
1978
1979        ShowStatus("The login-server is "CL_GREEN"ready"CL_RESET" (Server is listening on the port %u).\n\n", login_config.login_port);
1980
1981        return 0;
1982}
Note: See TracBrowser for help on using the browser.