root/src/login/login.c @ 1

Revision 1, 78.6 kB (checked in by jinshiro, 17 years ago)
RevLine 
[1]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/lock.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
44char GM_account_filename[1024] = "conf/GM_account.txt";
45long creation_time_GM_account_file; // tracks the last-changed timestamp of the gm accounts file
46int gm_account_filename_check_timer = 15; // Timer to check if GM_account file has been changed and reload GM account automaticaly (in seconds; default: 15)
47
48//Account registration flood protection [Kevin]
49int allowed_regs = 1;
50int time_allowed = 10; //in seconds
51unsigned int new_reg_tick = 0;
52
53
54// data handling (TXT)
55char account_filename[1024] = "save/account.txt";
56
57// account database
58struct mmo_account* auth_dat = NULL;
59unsigned int auth_num = 0, auth_max = 0;
60
61int account_id_count = START_ACCOUNT_NUM;
62
63// define the number of times that some players must authentify them before to save account file.
64// it's just about normal authentication. If an account is created or modified, save is immediatly done.
65// An authentication just change last connected IP and date. It already save in log file.
66// set minimum auth change before save:
67#define AUTH_BEFORE_SAVE_FILE 10
68// set divider of auth_num to found number of change before save
69#define AUTH_SAVE_FILE_DIVIDER 50
70int auth_before_save_file = 0; // Counter. First save when 1st char-server do connection.
71
72
73// ladmin configuration
74bool admin_state = false;
75char admin_pass[24] = "";
76uint32 admin_allowed_ip = 0;
77
78int parse_admin(int fd);
79
80
81//-----------------------------------------------------
82// Auth database
83//-----------------------------------------------------
84#define AUTH_TIMEOUT 30000
85
86struct auth_node {
87        int account_id;
88        uint32 login_id1;
89        uint32 login_id2;
90        uint32 ip;
91        char sex;
92};
93
94static DBMap* auth_db; // int account_id -> struct auth_node*
95
96//-----------------------------------------------------
97// Online User Database [Wizputer]
98//-----------------------------------------------------
99
100struct online_login_data {
101        int account_id;
102        int waiting_disconnect;
103        int char_server;
104};
105
106static DBMap* online_db; // int account_id -> struct online_login_data*
107static int waiting_disconnect_timer(int tid, unsigned int tick, int id, intptr data);
108
109static void* create_online_user(DBKey key, va_list args)
110{
111        struct online_login_data* p;
112        CREATE(p, struct online_login_data, 1);
113        p->account_id = key.i;
114        p->char_server = -1;
115        p->waiting_disconnect = -1;
116        return p;
117}
118
119struct online_login_data* add_online_user(int char_server, int account_id)
120{
121        struct online_login_data* p;
122        if( !login_config.online_check )
123                return NULL;
124        p = (struct online_login_data*)idb_ensure(online_db, account_id, create_online_user);
125        p->char_server = char_server;
126        if( p->waiting_disconnect != -1 )
127        {
128                delete_timer(p->waiting_disconnect, waiting_disconnect_timer);
129                p->waiting_disconnect = -1;
130        }
131        return p;
132}
133
134void remove_online_user(int account_id)
135{
136        struct online_login_data* p;
137        if( !login_config.online_check )
138                return;
139        p = (struct online_login_data*)idb_get(online_db, account_id);
140        if( p == NULL )
141                return;
142        if( p->waiting_disconnect != -1 )
143                delete_timer(p->waiting_disconnect, waiting_disconnect_timer);
144
145        idb_remove(online_db, account_id);
146}
147
148static int waiting_disconnect_timer(int tid, unsigned int tick, int id, intptr data)
149{
150        struct online_login_data* p = (struct online_login_data*)idb_get(online_db, id);
151        if( p != NULL && p->waiting_disconnect == tid && p->account_id == id )
152        {
153                p->waiting_disconnect = -1;
154                remove_online_user(id);
155                idb_remove(auth_db, id);
156        }
157        return 0;
158}
159
160//--------------------------------------------------------------------
161// Packet send to all char-servers, except one (wos: without our self)
162//--------------------------------------------------------------------
163int charif_sendallwos(int sfd, uint8* buf, size_t len)
164{
165        int i, c;
166
167        for( i = 0, c = 0; i < MAX_SERVERS; ++i )
168        {
169                int fd = server[i].fd;
170                if( session_isValid(fd) && fd != sfd )
171                {
172                        WFIFOHEAD(fd,len);
173                        memcpy(WFIFOP(fd,0), buf, len);
174                        WFIFOSET(fd,len);
175                        ++c;
176                }
177        }
178
179        return c;
180}
181
182//----------------------------------------------------------------------
183// Determine if an account (id) is a GM account
184// and returns its level (or 0 if it isn't a GM account or if not found)
185//----------------------------------------------------------------------
186int isGM(int account_id)
187{
188        unsigned int i;
189        ARR_FIND( 0, GM_num, i, gm_account_db[i].account_id == account_id );
190        return ( i < GM_num ) ? gm_account_db[i].level : 0;
191}
192
193//----------------------------------------------------------------------
194// Adds a new GM using acc id and level
195//----------------------------------------------------------------------
196void addGM(int account_id, int level)
197{
198        static unsigned int GM_max = 0;
199        unsigned int i;
200
201        ARR_FIND( 0, auth_num, i, auth_dat[i].account_id == account_id );
202        if( i == auth_num )
203                return; // no such account
204
205        ARR_FIND( 0, GM_num, i, gm_account_db[i].account_id == account_id );
206        if( i < GM_num )
207        {
208                if (gm_account_db[i].level == level)
209                        ShowWarning("addGM: GM account %d defined twice (same level: %d).\n", account_id, level);
210                else {
211                        ShowWarning("addGM: GM account %d defined twice (levels: %d and %d).\n", account_id, gm_account_db[i].level, level);
212                        gm_account_db[i].level = level;
213                }
214                return; // entry already present
215        }
216
217        // new account
218        if (GM_num >= GM_max) {
219                GM_max += 256;
220                RECREATE(gm_account_db, struct gm_account, GM_max);
221        }
222        gm_account_db[GM_num].account_id = account_id;
223        gm_account_db[GM_num].level = level;
224        GM_num++;
225        if (GM_num >= 4000)
226                ShowWarning("4000 GM accounts found. Next GM accounts are not read.\n");
227}
228
229//-------------------------------------------------------
230// Reading function of GM accounts file (and their level)
231//-------------------------------------------------------
232int read_gm_account(void)
233{
234        char line[512];
235        FILE *fp;
236        int account_id, level;
237        int line_counter;
238        struct stat file_stat;
239        int start_range = 0, end_range = 0, is_range = 0, current_id = 0;
240
241        if(gm_account_db) aFree(gm_account_db);
242        CREATE(gm_account_db, struct gm_account, 1);
243        GM_num = 0;
244
245        // get last modify time/date
246        if (stat(GM_account_filename, &file_stat))
247                creation_time_GM_account_file = 0; // error
248        else
249                creation_time_GM_account_file = (long)file_stat.st_mtime;
250
251        if ((fp = fopen(GM_account_filename, "r")) == NULL) {
252                ShowError("read_gm_account: GM accounts file [%s] not found.\n", GM_account_filename);
253                return 1;
254        }
255
256        line_counter = 0;
257        // limited to 4000, because we send information to char-servers (more than 4000 GM accounts???)
258        // int (id) + int (level) = 8 bytes * 4000 = 32k (limit of packets in windows)
259        while(fgets(line, sizeof(line), fp) && GM_num < 4000)
260        {
261                line_counter++;
262                if ((line[0] == '/' && line[1] == '/') || line[0] == '\0' || line[0] == '\n' || line[0] == '\r')
263                        continue;
264                is_range = (sscanf(line, "%d%*[-~]%d %d",&start_range,&end_range,&level)==3); // ID Range [MC Cameri]
265                if (!is_range && sscanf(line, "%d %d", &account_id, &level) != 2 && sscanf(line, "%d: %d", &account_id, &level) != 2)
266                        ShowError("read_gm_account: file [%s], invalid 'acount_id|range level' format (line #%d).\n", GM_account_filename, line_counter);
267                else if (level <= 0)
268                        ShowError("read_gm_account: file [%s] %dth account (line #%d) (invalid level [0 or negative]: %d).\n", GM_account_filename, GM_num+1, line_counter, level);
269                else {
270                        if (level > 99) {
271                                ShowNotice("read_gm_account: file [%s] %dth account (invalid level, but corrected: %d->99).\n", GM_account_filename, GM_num+1, level);
272                                level = 99;
273                        }
274                        if (is_range) {
275                                if (start_range==end_range)
276                                        ShowError("read_gm_account: file [%s] invalid range, beginning of range is equal to end of range (line #%d).\n", GM_account_filename, line_counter);
277                                else if (start_range>end_range)
278                                        ShowError("read_gm_account: file [%s] invalid range, beginning of range must be lower than end of range (line #%d).\n", GM_account_filename, line_counter);
279                                else
280                                        for (current_id = start_range;current_id<=end_range;current_id++)
281                                                addGM(current_id,level);
282                        } else {
283                                addGM(account_id,level);
284                        }
285                }
286        }
287        fclose(fp);
288
289        ShowStatus("read_gm_account: file '%s' read (%d GM accounts found).\n", GM_account_filename, GM_num);
290
291        return 0;
292}
293
294
295//-----------------------------------------------
296// Search an account id
297//   (return account index or -1 (if not found))
298//   If exact account name is not found,
299//   the function checks without case sensitive
300//   and returns index if only 1 account is found
301//   and similar to the searched name.
302//-----------------------------------------------
303int search_account_index(char* account_name)
304{
305        unsigned int i, quantity;
306        int index;
307
308        quantity = 0;
309        index = -1;
310
311        for(i = 0; i < auth_num; i++) {
312                // Without case sensitive check (increase the number of similar account names found)
313                if (stricmp(auth_dat[i].userid, account_name) == 0) {
314                        // Strict comparison (if found, we finish the function immediatly with correct value)
315                        if (strcmp(auth_dat[i].userid, account_name) == 0)
316                                return i;
317                        quantity++;
318                        index = i;
319                }
320        }
321        // Here, the exact account name is not found
322        // We return the found index of a similar account ONLY if there is 1 similar account
323        if (quantity == 1)
324                return index;
325
326        // Exact account name is not found and 0 or more than 1 similar accounts have been found ==> we say not found
327        return -1;
328}
329
330//--------------------------------------------------------
331// Create a string to save the account in the account file
332//--------------------------------------------------------
333int mmo_auth_tostr(char* str, struct mmo_account* p)
334{
335        int i;
336        char *str_p = str;
337
338        str_p += sprintf(str_p, "%d\t%s\t%s\t%s\t%c\t%d\t%u\t%s\t%s\t%ld\t%s\t%s\t%ld\t",
339                         p->account_id, p->userid, p->pass, p->lastlogin, p->sex,
340                         p->logincount, p->state, p->email, p->error_message,
341                         (long)p->expiration_time, p->last_ip, p->memo, (long)p->unban_time);
342
343        for(i = 0; i < p->account_reg2_num; i++)
344                if (p->account_reg2[i].str[0])
345                        str_p += sprintf(str_p, "%s,%s ", p->account_reg2[i].str, p->account_reg2[i].value);
346
347        return 0;
348}
349
350//---------------------------------
351// Reading of the accounts database
352//---------------------------------
353int mmo_auth_init(void)
354{
355        FILE *fp;
356        int account_id;
357        uint32 state;
358        int logincount, n;
359        uint32 i, j;
360        char line[2048], *p, userid[2048], pass[2048], lastlogin[2048], sex, email[2048], error_message[2048], last_ip[2048], memo[2048];
361        long unban_time;
362        long expiration_time;
363        char str[2048];
364        char v[2048];
365        int GM_count = 0;
366        int server_count = 0;
367
368        auth_max = 256;
369        CREATE(auth_dat, struct mmo_account, auth_max);
370
371        if ((fp = fopen(account_filename, "r")) == NULL) {
372                // no account file -> no account -> no login, including char-server (ERROR)
373                ShowError(CL_RED"mmmo_auth_init: Accounts file [%s] not found."CL_RESET"\n", account_filename);
374                return 0;
375        }
376
377        while(fgets(line, sizeof(line), fp) != NULL)
378        {
379                if (line[0] == '/' && line[1] == '/')
380                        continue;
381
382                p = line;
383
384                memset(userid, 0, sizeof(userid));
385                memset(pass, 0, sizeof(pass));
386                memset(lastlogin, 0, sizeof(lastlogin));
387                memset(email, 0, sizeof(email));
388                memset(error_message, 0, sizeof(error_message));
389                memset(last_ip, 0, sizeof(last_ip));
390                memset(memo, 0, sizeof(memo));
391
392                // database version reading (v2)
393                if (((i = sscanf(line, "%d\t%[^\t]\t%[^\t]\t%[^\t]\t%c\t%d\t%u\t"
394                                 "%[^\t]\t%[^\t]\t%ld\t%[^\t]\t%[^\t]\t%ld%n",
395                    &account_id, userid, pass, lastlogin, &sex, &logincount, &state,
396                    email, error_message, &expiration_time, last_ip, memo, &unban_time, &n)) == 13 && line[n] == '\t') ||
397                    ((i = sscanf(line, "%d\t%[^\t]\t%[^\t]\t%[^\t]\t%c\t%d\t%u\t"
398                                 "%[^\t]\t%[^\t]\t%ld\t%[^\t]\t%[^\t]%n",
399                    &account_id, userid, pass, lastlogin, &sex, &logincount, &state,
400                    email, error_message, &expiration_time, last_ip, memo, &n)) == 12 && line[n] == '\t')) {
401                        n = n + 1;
402
403                        // Some checks
404                        if (account_id > END_ACCOUNT_NUM) {
405                                ShowError(CL_RED"mmmo_auth_init: an account has an id higher than %d\n", END_ACCOUNT_NUM);
406                                ShowError("               account id #%d -> account not read (data is lost!)."CL_RESET"\n", account_id);
407                                continue;
408                        }
409                        userid[23] = '\0';
410                        remove_control_chars(userid);
411                        for(j = 0; j < auth_num; j++) {
412                                if (auth_dat[j].account_id == account_id) {
413                                        ShowError(CL_RED"mmmo_auth_init: an account has an identical id to another.\n");
414                                        ShowError("               account id #%d -> new account not read (data is lost!)."CL_RED"\n", account_id);
415                                        break;
416                                } else if (strcmp(auth_dat[j].userid, userid) == 0) {
417                                        ShowError(CL_RED"mmmo_auth_init: account name already exists.\n");
418                                        ShowError("               account name '%s' -> new account not read (data is lost!)."CL_RESET"\n", userid); // 2 lines, account name can be long.
419                                        break;
420                                }
421                        }
422                        if (j != auth_num)
423                                continue;
424
425                        if (auth_num >= auth_max) {
426                                auth_max += 256;
427                                auth_dat = (struct mmo_account*)aRealloc(auth_dat, sizeof(struct mmo_account) * auth_max);
428                        }
429
430                        memset(&auth_dat[auth_num], '\0', sizeof(struct mmo_account));
431
432                        auth_dat[auth_num].account_id = account_id;
433
434                        strncpy(auth_dat[auth_num].userid, userid, 24);
435
436                        pass[32] = '\0';
437                        remove_control_chars(pass);
438                        strncpy(auth_dat[auth_num].pass, pass, 32);
439
440                        lastlogin[23] = '\0';
441                        remove_control_chars(lastlogin);
442                        strncpy(auth_dat[auth_num].lastlogin, lastlogin, 24);
443
444                        auth_dat[auth_num].sex = sex;
445
446                        if (logincount >= 0)
447                                auth_dat[auth_num].logincount = logincount;
448                        else
449                                auth_dat[auth_num].logincount = 0;
450
451                        if (state > 255)
452                                auth_dat[auth_num].state = 100;
453                        else
454                                auth_dat[auth_num].state = state;
455
456                        if (e_mail_check(email) == 0) {
457                                ShowNotice("Account %s (%d): invalid e-mail (replaced par a@a.com).\n", auth_dat[auth_num].userid, auth_dat[auth_num].account_id);
458                                strncpy(auth_dat[auth_num].email, "a@a.com", 40);
459                        } else {
460                                remove_control_chars(email);
461                                strncpy(auth_dat[auth_num].email, email, 40);
462                        }
463
464                        error_message[19] = '\0';
465                        remove_control_chars(error_message);
466                        if (error_message[0] == '\0' || state != 7) { // 7, because state is packet 0x006a value + 1
467                                strncpy(auth_dat[auth_num].error_message, "-", 20);
468                        } else {
469                                strncpy(auth_dat[auth_num].error_message, error_message, 20);
470                        }
471
472                        if (i == 13)
473                                auth_dat[auth_num].unban_time = (time_t)unban_time;
474                        else
475                                auth_dat[auth_num].unban_time = 0;
476
477                        auth_dat[auth_num].expiration_time = (time_t)expiration_time;
478
479                        last_ip[15] = '\0';
480                        remove_control_chars(last_ip);
481                        strncpy(auth_dat[auth_num].last_ip, last_ip, 16);
482
483                        memo[254] = '\0';
484                        remove_control_chars(memo);
485                        strncpy(auth_dat[auth_num].memo, memo, 255);
486
487                        for(j = 0; j < ACCOUNT_REG2_NUM; j++) {
488                                p += n;
489                                if (sscanf(p, "%[^\t,],%[^\t ] %n", str, v, &n) != 2) { 
490                                        // We must check if a str is void. If it's, we can continue to read other REG2.
491                                        // Account line will have something like: str2,9 ,9 str3,1 (here, ,9 is not good)
492                                        if (p[0] == ',' && sscanf(p, ",%[^\t ] %n", v, &n) == 1) { 
493                                                j--;
494                                                continue;
495                                        } else
496                                                break;
497                                }
498                                str[31] = '\0';
499                                remove_control_chars(str);
500                                strncpy(auth_dat[auth_num].account_reg2[j].str, str, 32);
501                                strncpy(auth_dat[auth_num].account_reg2[j].value,v,256);
502                        }
503                        auth_dat[auth_num].account_reg2_num = j;
504
505                        if (isGM(account_id) > 0)
506                                GM_count++;
507                        if (auth_dat[auth_num].sex == 'S')
508                                server_count++;
509
510                        auth_num++;
511                        if (account_id >= account_id_count)
512                                account_id_count = account_id + 1;
513
514                // Old athena database version reading (v1)
515                } else if ((i = sscanf(line, "%d\t%[^\t]\t%[^\t]\t%[^\t]\t%c\t%d\t%u\t%n",
516                           &account_id, userid, pass, lastlogin, &sex, &logincount, &state, &n)) >= 5) {
517                        if (account_id > END_ACCOUNT_NUM) {
518                                ShowError(CL_RED"mmmo_auth_init: an account has an id higher than %d\n", END_ACCOUNT_NUM);
519                                ShowError("               account id #%d -> account not read (data is lost!)."CL_RESET"\n", account_id);
520                                continue;
521                        }
522                        userid[23] = '\0';
523                        remove_control_chars(userid);
524                        for(j = 0; j < auth_num; j++) {
525                                if (auth_dat[j].account_id == account_id) {
526                                        ShowError(CL_RED"mmo_auth_init: an account has an identical id to another.\n");
527                                        ShowError("               account id #%d -> new account not read (data is lost!)."CL_RESET"\n", account_id);
528                                        break;
529                                } else if (strcmp(auth_dat[j].userid, userid) == 0) {
530                                        ShowError(CL_RED"mmo_auth_init: account name already exists.\n");
531                                        ShowError("               account name '%s' -> new account not read (data is lost!)."CL_RESET"\n", userid);
532                                        break;
533                                }
534                        }
535                        if (j != auth_num)
536                                continue;
537
538                        if (auth_num >= auth_max) {
539                                auth_max += 256;
540                                RECREATE(auth_dat, struct mmo_account, auth_max);
541                        }
542
543                        memset(&auth_dat[auth_num], '\0', sizeof(struct mmo_account));
544
545                        auth_dat[auth_num].account_id = account_id;
546
547                        strncpy(auth_dat[auth_num].userid, userid, 24);
548
549                        pass[23] = '\0';
550                        remove_control_chars(pass);
551                        strncpy(auth_dat[auth_num].pass, pass, 24);
552
553                        lastlogin[23] = '\0';
554                        remove_control_chars(lastlogin);
555                        strncpy(auth_dat[auth_num].lastlogin, lastlogin, 24);
556
557                        auth_dat[auth_num].sex = sex;
558
559                        if (i >= 6) {
560                                if (logincount >= 0)
561                                        auth_dat[auth_num].logincount = logincount;
562                                else
563                                        auth_dat[auth_num].logincount = 0;
564                        } else
565                                auth_dat[auth_num].logincount = 0;
566
567                        if (i >= 7) {
568                                if (state > 255)
569                                        auth_dat[auth_num].state = 100;
570                                else
571                                        auth_dat[auth_num].state = state;
572                        } else
573                                auth_dat[auth_num].state = 0;
574
575                        // Initialization of new data
576                        strncpy(auth_dat[auth_num].email, "a@a.com", 40);
577                        strncpy(auth_dat[auth_num].error_message, "-", 20);
578                        auth_dat[auth_num].unban_time = 0;
579                        auth_dat[auth_num].expiration_time = 0;
580                        strncpy(auth_dat[auth_num].last_ip, "-", 16);
581                        strncpy(auth_dat[auth_num].memo, "-", 255);
582
583                        for(j = 0; j < ACCOUNT_REG2_NUM; j++) {
584                                p += n;
585                                if (sscanf(p, "%[^\t,],%[^\t ] %n", str, v, &n) != 2) { 
586                                        // We must check if a str is void. If it's, we can continue to read other REG2.
587                                        // Account line will have something like: str2,9 ,9 str3,1 (here, ,9 is not good)
588                                        if (p[0] == ',' && sscanf(p, ",%[^\t ] %n", v, &n) == 1) { 
589                                                j--;
590                                                continue;
591                                        } else
592                                                break;
593                                }
594                                str[31] = '\0';
595                                remove_control_chars(str);
596                                strncpy(auth_dat[auth_num].account_reg2[j].str, str, 32);
597                                strncpy(auth_dat[auth_num].account_reg2[j].value,v,256);
598                        }
599                        auth_dat[auth_num].account_reg2_num = j;
600
601                        if (isGM(account_id) > 0)
602                                GM_count++;
603                        if (auth_dat[auth_num].sex == 'S')
604                                server_count++;
605
606                        auth_num++;
607                        if (account_id >= account_id_count)
608                                account_id_count = account_id + 1;
609
610                } else {
611                        int i = 0;
612                        if (sscanf(line, "%d\t%%newid%%\n%n", &account_id, &i) == 1 &&
613                            i > 0 && account_id > account_id_count)
614                                account_id_count = account_id;
615                }
616        }
617        fclose(fp);
618
619        if( auth_num == 0 )
620                ShowNotice("mmo_auth_init: No account found in %s.\n", account_filename);
621        else
622        if( auth_num == 1 )
623                ShowStatus("mmo_auth_init: 1 account read in %s,\n", account_filename);
624        else
625                ShowStatus("mmo_auth_init: %d accounts read in %s,\n", auth_num, account_filename);
626
627        if( GM_count == 0 )
628                ShowStatus("               of which is no GM account, and \n");
629        else
630        if( GM_count == 1 )
631                ShowStatus("               of which is 1 GM account, and \n");
632        else
633                ShowStatus("               of which is %d GM accounts, and \n", GM_count);
634
635        if( server_count == 0 )
636                ShowStatus("               no server account ('S').\n");
637        else
638        if( server_count == 1 )
639                ShowStatus("               1 server account ('S').\n");
640        else
641                ShowStatus("               %d server accounts ('S').\n", server_count);
642
643        return 0;
644}
645
646//------------------------------------------
647// Writing of the accounts database file
648//   (accounts are sorted by id before save)
649//------------------------------------------
650void mmo_auth_sync(void)
651{
652        FILE *fp;
653        unsigned int i, j, k;
654        int lock;
655        int account_id;
656        CREATE_BUFFER(id, int, auth_num);
657        char line[65536];
658
659        // Sorting before save
660        for(i = 0; i < auth_num; i++) {
661                id[i] = i;
662                account_id = auth_dat[i].account_id;
663                for(j = 0; j < i; j++) {
664                        if (account_id < auth_dat[id[j]].account_id) {
665                                for(k = i; k > j; k--)
666                                        id[k] = id[k-1];
667                                id[j] = i; // id[i]
668                                break;
669                        }
670                }
671        }
672
673        // Data save
674        if ((fp = lock_fopen(account_filename, &lock)) == NULL) {
675                //if (id) aFree(id);
676                DELETE_BUFFER(id);
677                return;
678        }
679
680        fprintf(fp, "// Accounts file: here are saved all information about the accounts.\n");
681        fprintf(fp, "// Structure: ID, account name, password, last login time, sex, # of logins, state, email, error message for state 7, validity time, last (accepted) login ip, memo field, ban timestamp, repeated(register text, register value)\n");
682        fprintf(fp, "// Some explanations:\n");
683        fprintf(fp, "//   account name    : between 4 to 23 char for a normal account (standard client can't send less than 4 char).\n");
684        fprintf(fp, "//   account password: between 4 to 23 char\n");
685        fprintf(fp, "//   sex             : M or F for normal accounts, S for server accounts\n");
686        fprintf(fp, "//   state           : 0: account is ok, 1 to 256: error code of packet 0x006a + 1\n");
687        fprintf(fp, "//   email           : between 3 to 39 char (a@a.com is like no email)\n");
688        fprintf(fp, "//   error message   : text for the state 7: 'Your are Prohibited to login until <text>'. Max 19 char\n");
689        fprintf(fp, "//   valitidy time   : 0: unlimited account, <other value>: date calculated by addition of 1/1/1970 + value (number of seconds since the 1/1/1970)\n");
690        fprintf(fp, "//   memo field      : max 254 char\n");
691        fprintf(fp, "//   ban time        : 0: no ban, <other value>: banned until the date: date calculated by addition of 1/1/1970 + value (number of seconds since the 1/1/1970)\n");
692        for(i = 0; i < auth_num; i++) {
693                k = id[i]; // use of sorted index
694                if (auth_dat[k].account_id == -1)
695                        continue;
696
697                mmo_auth_tostr(line, &auth_dat[k]);
698                fprintf(fp, "%s\n", line);
699        }
700        fprintf(fp, "%d\t%%newid%%\n", account_id_count);
701
702        lock_fclose(fp, account_filename, &lock);
703
704        // set new counter to minimum number of auth before save
705        auth_before_save_file = auth_num / AUTH_SAVE_FILE_DIVIDER; // Re-initialise counter. We have save.
706        if (auth_before_save_file < AUTH_BEFORE_SAVE_FILE)
707                auth_before_save_file = AUTH_BEFORE_SAVE_FILE;
708
709        //if (id) aFree(id);
710        DELETE_BUFFER(id);
711
712        return;
713}
714
715//-----------------------------------------------------
716// Check if we must save accounts file or not
717//   every minute, we check if we must save because we
718//   have do some authentications without arrive to
719//   the minimum of authentications for the save.
720// Note: all other modification of accounts (deletion,
721//       change of some informations excepted lastip/
722//       lastlogintime, creation) are always save
723//       immediatly and set  the minimum of
724//       authentications to its initialization value.
725//-----------------------------------------------------
726int check_auth_sync(int tid, unsigned int tick, int id, intptr data)
727{
728        // we only save if necessary:
729        // we have do some authentications without do saving
730        if (auth_before_save_file < AUTH_BEFORE_SAVE_FILE ||
731            auth_before_save_file < (int)(auth_num / AUTH_SAVE_FILE_DIVIDER))
732                mmo_auth_sync();
733
734        return 0;
735}
736
737//-----------------------------------------------------
738// periodic ip address synchronization
739//-----------------------------------------------------
740static int sync_ip_addresses(int tid, unsigned int tick, int id, intptr data)
741{
742        uint8 buf[2];
743        ShowInfo("IP Sync in progress...\n");
744        WBUFW(buf,0) = 0x2735;
745        charif_sendallwos(-1, buf, 2);
746        return 0;
747}
748
749//-----------------------------------------------------
750// Send GM accounts to one or all char-servers
751//-----------------------------------------------------
752void send_GM_accounts(int fd)
753{
754        unsigned int i;
755        uint8 buf[32767];
756        uint16 len;
757
758        len = 4;
759        WBUFW(buf,0) = 0x2732;
760        for(i = 0; i < GM_num; i++)
761                // send only existing accounts. We can not create a GM account when server is online.
762                if (gm_account_db[i].level > 0) {
763                        WBUFL(buf,len) = gm_account_db[i].account_id;
764                        WBUFB(buf,len+4) = (uint8)gm_account_db[i].level;
765                        len += 5;
766                        if (len >= 32000) {
767                                ShowWarning("send_GM_accounts: Too many accounts! Only %d out of %d were sent.\n", i, GM_num);
768                                break;
769                        }
770                }
771
772        WBUFW(buf,2) = len;
773        if (fd == -1) // send to all charservers
774                charif_sendallwos(-1, buf, len);
775        else { // send only to target
776                WFIFOHEAD(fd,len);
777                memcpy(WFIFOP(fd,0), buf, len);
778                WFIFOSET(fd,len);
779        }
780
781        return;
782}
783
784//-----------------------------------------------------
785// Check if GM file account have been changed
786//-----------------------------------------------------
787int check_GM_file(int tid, unsigned int tick, int id, intptr data)
788{
789        struct stat file_stat;
790        long new_time;
791
792        // if we would not check
793        if (gm_account_filename_check_timer < 1)
794                return 0;
795
796        // get last modify time/date
797        if (stat(GM_account_filename, &file_stat))
798                new_time = 0; // error
799        else
800                new_time = (long)file_stat.st_mtime;
801
802        if (new_time != creation_time_GM_account_file) {
803                read_gm_account();
804                send_GM_accounts(-1);
805        }
806
807        return 0;
808}
809
810
811//-----------------------------------------------------
812// encrypted/unencrypted password check
813//-----------------------------------------------------
814bool check_encrypted(const char* str1, const char* str2, const char* passwd)
815{
816        char md5str[64], md5bin[32];
817
818        snprintf(md5str, sizeof(md5str), "%s%s", str1, str2);
819        md5str[sizeof(md5str)-1] = '\0';
820        MD5_String2binary(md5str, md5bin);
821
822        return (0==memcmp(passwd, md5bin, 16));
823}
824
825bool check_password(struct login_session_data* sd, int passwdenc, const char* passwd, const char* refpass)
826{       
827        if(passwdenc == 0)
828        {
829                return (0==strcmp(passwd, refpass));
830        }
831        else if(sd != NULL)
832        {
833                // password mode set to 1 -> (md5key, refpass) enable with <passwordencrypt></passwordencrypt>
834                // password mode set to 2 -> (refpass, md5key) enable with <passwordencrypt2></passwordencrypt2>
835               
836                return ((passwdenc&0x01) && check_encrypted(sd->md5key, refpass, passwd)) ||
837                       ((passwdenc&0x02) && check_encrypted(refpass, sd->md5key, passwd));
838        }
839        return false;
840}
841
842
843//-------------------------------------
844// Make new account
845//-------------------------------------
846int mmo_auth_new(struct mmo_account* account)
847{
848        static int num_regs = 0; // registration counter
849        unsigned int tick = gettick();
850
851        time_t expiration_time = 0;
852        unsigned int i = auth_num;
853
854        // check if the account doesn't exist already
855        ARR_FIND( 0, auth_num, i, strcmp(account->userid, auth_dat[i].userid) == 0 );
856        if( i < auth_num )
857        {
858                ShowNotice("Attempt of creation of an already existant account (account: %s_%c, pass: %s, received pass: %s)\n", account->userid, account->sex, auth_dat[i].pass, account->pass);
859                return 1; // 1 = Incorrect Password
860        }
861
862        //Account Registration Flood Protection by [Kevin]
863        if( DIFF_TICK(tick, new_reg_tick) < 0 && num_regs >= allowed_regs )
864        {
865                ShowNotice("Account registration denied (registration limit exceeded)\n");
866                return 3;
867        }
868
869        if (auth_num >= auth_max) {
870                auth_max += 256;
871                auth_dat = (struct mmo_account*)aRealloc(auth_dat, sizeof(struct mmo_account) * auth_max);
872        }
873
874        memset(&auth_dat[i], '\0', sizeof(struct mmo_account));
875
876        // find a suitable non-gm account id
877        while (isGM(account_id_count) > 0)
878                account_id_count++;
879
880        auth_dat[i].account_id = account_id_count++;
881        safestrncpy(auth_dat[i].userid, account->userid, NAME_LENGTH);
882        if( login_config.use_md5_passwds )
883                MD5_String(account->pass, auth_dat[i].pass);
884        else
885                safestrncpy(auth_dat[i].pass, account->pass, NAME_LENGTH);
886        safestrncpy(auth_dat[i].lastlogin, "-", sizeof(auth_dat[i].lastlogin));
887        auth_dat[i].sex = account->sex;
888        auth_dat[i].logincount = 0;
889        auth_dat[i].state = 0;
890        safestrncpy(auth_dat[i].email, e_mail_check(account->email) ? account->email : "a@a.com", sizeof(auth_dat[i].email));
891        safestrncpy(auth_dat[i].error_message, "-", sizeof(auth_dat[i].error_message));
892        auth_dat[i].unban_time = 0;
893        if( login_config.start_limited_time != -1 )
894                expiration_time = time(NULL) + login_config.start_limited_time;
895        auth_dat[i].expiration_time = expiration_time;
896        strncpy(auth_dat[i].last_ip, "-", 16);
897        strncpy(auth_dat[i].memo, "-", 255);
898        auth_dat[i].account_reg2_num = 0;
899
900        ShowNotice("Account creation (account %s, id: %d, pass: %s, sex: %c)\n", account->userid, auth_num, account->pass, account->sex);
901        auth_num++;
902
903        if( DIFF_TICK(tick, new_reg_tick) > 0 )
904        {// Update the registration check.
905                num_regs = 0;
906                new_reg_tick = tick + time_allowed*1000;
907        }
908        ++num_regs;
909
910        return 0;
911}
912
913//-----------------------------------------------------
914// Check/authentication of a connection
915//-----------------------------------------------------
916int mmo_auth(struct login_session_data* sd)
917{
918        unsigned int i;
919        time_t raw_time;
920        char tmpstr[256];
921        size_t len;
922        char user_password[32+1]; // reserve for md5-ed pw
923
924        char ip[16];
925        ip2str(session[sd->fd]->client_addr, ip);
926
927        // DNS Blacklist check
928        if( login_config.use_dnsbl )
929        {
930                char r_ip[16];
931                char ip_dnsbl[256];
932                char* dnsbl_serv;
933                bool matched = false;
934                uint8* sin_addr = (uint8*)&session[sd->fd]->client_addr;
935
936                sprintf(r_ip, "%u.%u.%u.%u", sin_addr[0], sin_addr[1], sin_addr[2], sin_addr[3]);
937
938                for( dnsbl_serv = strtok(login_config.dnsbl_servs,","); !matched && dnsbl_serv != NULL; dnsbl_serv = strtok(NULL,",") )
939                {
940                        sprintf(ip_dnsbl, "%s.%s", r_ip, dnsbl_serv);
941                        if( host2ip(ip_dnsbl) )
942                                matched = true;
943                }
944
945                if( matched )
946                {
947                        ShowInfo("DNSBL: (%s) Blacklisted. User Kicked.\n", r_ip);
948                        return 3;
949                }
950        }
951
952        //Client Version check
953        if( login_config.check_client_version && sd->version != login_config.client_version_to_connect )
954                return 5;
955
956        len = strnlen(sd->userid, NAME_LENGTH);
957
958        // Account creation with _M/_F
959        if( login_config.new_account_flag )
960        {
961                if( len > 2 && strnlen(sd->passwd, NAME_LENGTH) > 0 && // valid user and password lengths
962                        sd->passwdenc == 0 && // unencoded password
963                        sd->userid[len-2] == '_' && memchr("FfMm", sd->userid[len-1], 4) && // _M/_F suffix
964                        account_id_count <= END_ACCOUNT_NUM )
965                {
966                        struct mmo_account acc;
967                        int result;
968
969                        len -= 2;
970                        sd->userid[len] = '\0';
971
972                        memset(&acc, '\0', sizeof(acc));
973                        safestrncpy(acc.userid, sd->userid, NAME_LENGTH);
974                        safestrncpy(acc.pass, sd->passwd, NAME_LENGTH);
975                        safestrncpy(acc.email, "a@a.com", sizeof(acc.email));
976                        acc.sex = TOUPPER(sd->userid[len+1]);
977
978                        result = mmo_auth_new(&acc);
979                        if( result )
980                                return result;// Failed to make account. [Skotlex].
981
982                        auth_before_save_file = 0; // Creation of an account -> save accounts file immediatly
983                }
984        }
985       
986        // Strict account search
987        ARR_FIND( 0, auth_num, i, strcmp(sd->userid, auth_dat[i].userid) == 0 );
988
989        // if strict account search fails, we do a no sensitive case research for index
990        if( i < auth_num )
991        {
992                i = search_account_index(sd->userid);
993                if( i == -1 )
994                        i = auth_num;
995                else
996                        memcpy(sd->userid, auth_dat[i].userid, NAME_LENGTH); // for the possible tests/checks afterwards (copy correcte sensitive case).
997        }
998
999        if( i == auth_num )
1000        {
1001                ShowNotice("Unknown account (account: %s, received pass: %s, ip: %s)\n", sd->userid, sd->passwd, ip);
1002                return 0; // 0 = Unregistered ID
1003        }
1004
1005        if( login_config.use_md5_passwds )
1006                MD5_String(sd->passwd, user_password);
1007        else
1008                safestrncpy(user_password, sd->passwd, NAME_LENGTH);
1009
1010        if( !check_password(sd, sd->passwdenc, user_password, auth_dat[i].pass) )
1011        {
1012                ShowNotice("Invalid password (account: %s, pass: %s, received pass: %s, ip: %s)\n", sd->userid, auth_dat[i].pass, (sd->passwdenc) ? "[MD5]" : sd->passwd, ip);
1013                return 1; // 1 = Incorrect Password
1014        }
1015
1016        if( auth_dat[i].expiration_time != 0 && auth_dat[i].expiration_time < time(NULL) )
1017        {
1018                ShowNotice("Connection refused (account: %s, pass: %s, expired ID, ip: %s)\n", sd->userid, sd->passwd, ip);
1019                return 2; // 2 = This ID is expired
1020        }
1021
1022        if( auth_dat[i].unban_time != 0 && auth_dat[i].unban_time > time(NULL) )
1023        {
1024                strftime(tmpstr, 20, login_config.date_format, localtime(&auth_dat[i].unban_time));
1025                tmpstr[19] = '\0';
1026                ShowNotice("Connection refused (account: %s, pass: %s, banned until %s, ip: %s)\n", sd->userid, sd->passwd, tmpstr, ip);
1027                return 6; // 6 = Your are Prohibited to log in until %s
1028        }
1029
1030        if( auth_dat[i].state )
1031        {
1032                ShowNotice("Connection refused (account: %s, pass: %s, state: %d, ip: %s)\n", sd->userid, sd->passwd, auth_dat[i].state, ip);
1033                return auth_dat[i].state - 1;
1034        }
1035
1036        ShowNotice("Authentication accepted (account: %s, id: %d, ip: %s)\n", sd->userid, auth_dat[i].account_id, ip);
1037
1038        // auth start : time seed
1039        time(&raw_time);
1040        strftime(tmpstr, 24, "%Y-%m-%d %H:%M:%S",localtime(&raw_time));
1041
1042        sd->account_id = auth_dat[i].account_id;
1043        sd->login_id1 = rand();
1044        sd->login_id2 = rand();
1045        safestrncpy(sd->lastlogin, auth_dat[i].lastlogin, 24);
1046        sd->sex = auth_dat[i].sex;
1047
1048        if( sd->sex != 'S' && sd->account_id < START_ACCOUNT_NUM )
1049                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);
1050
1051        safestrncpy(auth_dat[i].lastlogin, tmpstr, sizeof(auth_dat[i].lastlogin));
1052        safestrncpy(auth_dat[i].last_ip, ip, sizeof(auth_dat[i].last_ip));
1053        auth_dat[i].unban_time = 0;
1054        auth_dat[i].logincount++;
1055
1056        // Save until for change ip/time of auth is not very useful => limited save for that
1057        // Save there informations isnot necessary, because they are saved in log file.
1058        if (--auth_before_save_file <= 0) // Reduce counter. 0 or less, we save
1059                mmo_auth_sync();
1060
1061        return -1; // account OK
1062}
1063
1064static int online_db_setoffline(DBKey key, void* data, va_list ap)
1065{
1066        struct online_login_data* p = (struct online_login_data*)data;
1067        int server = va_arg(ap, int);
1068        if( server == -1 )
1069        {
1070                p->char_server = -1;
1071                if( p->waiting_disconnect != -1 )
1072                {
1073                        delete_timer(p->waiting_disconnect, waiting_disconnect_timer);
1074                        p->waiting_disconnect = -1;
1075                }
1076        }
1077        else if( p->char_server == server )
1078                p->char_server = -2; //Char server disconnected.
1079        return 0;
1080}
1081
1082//--------------------------------
1083// Packet parsing for char-servers
1084//--------------------------------
1085int parse_fromchar(int fd)
1086{
1087        unsigned int i;
1088        int j, id;
1089        uint32 ipl;
1090        char ip[16];
1091
1092        ARR_FIND( 0, MAX_SERVERS, id, server[id].fd == fd );
1093        if( id == MAX_SERVERS )
1094        {// not a char server
1095                set_eof(fd);
1096                do_close(fd);
1097                return 0;
1098        }
1099
1100        if( session[fd]->flag.eof )
1101        {
1102                ShowStatus("Char-server '%s' has disconnected.\n", server[id].name);
1103                online_db->foreach(online_db, online_db_setoffline, id); //Set all chars from this char server to offline.
1104                memset(&server[id], 0, sizeof(struct mmo_char_server));
1105                server[id].fd = -1;
1106                do_close(fd);
1107                return 0;
1108        }
1109
1110        ipl = server[id].ip;
1111        ip2str(ipl, ip);
1112
1113        while( RFIFOREST(fd) >= 2 )
1114        {
1115                uint16 command = RFIFOW(fd,0);
1116
1117                switch( command )
1118                {
1119
1120                case 0x2709: // request from map-server via char-server to reload GM accounts
1121                        RFIFOSKIP(fd,2);
1122                        ShowStatus("Char-server '%s': Request to re-load GM configuration file (ip: %s).\n", server[id].name, ip);
1123                        read_gm_account();
1124                        // send GM accounts to all char-servers
1125                        send_GM_accounts(-1);
1126                break;
1127
1128                case 0x2712: // request from char-server to authenticate an account
1129                        if( RFIFOREST(fd) < 19 )
1130                                return 0;
1131                {
1132                        struct auth_node* node;
1133
1134                        int account_id = RFIFOL(fd,2);
1135                        int login_id1 = RFIFOL(fd,6);
1136                        int login_id2 = RFIFOL(fd,10);
1137                        char sex = sex_num2str(RFIFOB(fd,14));
1138                        uint32 ip_ = ntohl(RFIFOL(fd,15));
1139                        RFIFOSKIP(fd,19);
1140
1141                        node = (struct auth_node*)idb_get(auth_db, account_id);
1142                        if( node != NULL &&
1143                            node->account_id == account_id &&
1144                                node->login_id1  == login_id1 &&
1145                                node->login_id2  == login_id2 &&
1146                                node->sex        == sex &&
1147                                node->ip         == ip_ )
1148                        {// found
1149                                uint32 expiration_time;
1150                                char email[40];
1151                                unsigned int k;
1152
1153                                //ShowStatus("Char-server '%s': authentication of the account %d accepted (ip: %s).\n", server[id].name, account_id, ip);
1154
1155                                // each auth entry can only be used once
1156                                idb_remove(auth_db, account_id);
1157
1158                                // retrieve email and account expiration time
1159                                ARR_FIND( 0, auth_num, k, auth_dat[k].account_id == account_id );
1160                                if( k < auth_num )
1161                                {
1162                                        strcpy(email, auth_dat[k].email);
1163                                        expiration_time = (uint32)auth_dat[k].expiration_time;
1164                                }
1165                                else
1166                                {
1167                                        memset(email, 0, sizeof(email));
1168                                        expiration_time = 0;
1169                                }
1170
1171                                // send ack
1172                                WFIFOHEAD(fd,59);
1173                                WFIFOW(fd,0) = 0x2713;
1174                                WFIFOL(fd,2) = account_id;
1175                                WFIFOL(fd,6) = login_id1;
1176                                WFIFOL(fd,10) = login_id2;
1177                                WFIFOB(fd,14) = 0;
1178                                memcpy(WFIFOP(fd,15), email, 40);
1179                                WFIFOL(fd,55) = expiration_time;
1180                                WFIFOSET(fd,59);
1181                        }
1182                        else
1183                        {// authentication not found
1184                                ShowStatus("Char-server '%s': authentication of the account %d REFUSED (ip: %s).\n", server[id].name, account_id, ip);
1185                                WFIFOHEAD(fd,59);
1186                                WFIFOW(fd,0) = 0x2713;
1187                                WFIFOL(fd,2) = account_id;
1188                                WFIFOL(fd,6) = login_id1;
1189                                WFIFOL(fd,10) = login_id2;
1190                                WFIFOB(fd,14) = 1;
1191                                // It is unnecessary to send email
1192                                // It is unnecessary to send validity date of the account
1193                                WFIFOSET(fd,59);
1194                        }
1195                }
1196                break;
1197
1198                case 0x2714:
1199                        if( RFIFOREST(fd) < 6 )
1200                                return 0;
1201                {
1202                        int users = RFIFOL(fd,2);
1203                        RFIFOSKIP(fd,6);
1204
1205                        // how many users on world? (update)
1206                        if( server[id].users != users )
1207                        {
1208                                ShowStatus("set users %s : %d\n", server[id].name, users);
1209
1210                                server[id].users = users;
1211                        }
1212                }
1213                break;
1214
1215                case 0x2715: // request from char server to change e-email from default "a@a.com"
1216                        if (RFIFOREST(fd) < 46)
1217                                return 0;
1218                {
1219                        char email[40];
1220                        int acc = RFIFOL(fd,2);
1221                        safestrncpy(email, (char*)RFIFOP(fd,6), 40); remove_control_chars(email);
1222                        RFIFOSKIP(fd,46);
1223
1224                        if( e_mail_check(email) == 0 )
1225                                ShowNotice("Char-server '%s': Attempt to create an e-mail on an account with a default e-mail REFUSED - e-mail is invalid (account: %d, ip: %s)\n", server[id].name, acc, ip);
1226                        else
1227                        {
1228                                ARR_FIND( 0, auth_num, i, auth_dat[i].account_id == acc && (strcmp(auth_dat[i].email, "a@a.com") == 0 || auth_dat[i].email[0] == '\0') );
1229                                if( i == auth_num )
1230                                        ShowNotice("Char-server '%s': Attempt to create an e-mail on an account with a default e-mail REFUSED - account doesn't exist or e-mail of account isn't default e-mail (account: %d, ip: %s).\n", server[id].name, acc, ip);
1231                                else
1232                                {
1233                                        memcpy(auth_dat[i].email, email, 40);
1234                                        ShowNotice("Char-server '%s': Create an e-mail on an account with a default e-mail (account: %d, new e-mail: %s, ip: %s).\n", server[id].name, acc, email, ip);
1235                                        // Save
1236                                        mmo_auth_sync();
1237                                }
1238                        }
1239                }
1240                break;
1241
1242                case 0x2716: // received an e-mail/limited time request, because a player comes back from a map-server to the char-server
1243                        if( RFIFOREST(fd) < 6 )
1244                                return 0;
1245                {
1246                        uint32 expiration_time = 0;
1247                        char email[40] = "";
1248
1249                        int account_id = RFIFOL(fd,2);
1250                        RFIFOSKIP(fd,6);
1251
1252                        ARR_FIND( 0, auth_num, i, auth_dat[i].account_id == account_id );
1253                        if( i == auth_num )
1254                                ShowNotice("Char-server '%s': e-mail of the account %d NOT found (ip: %s).\n", server[id].name, RFIFOL(fd,2), ip);
1255                        else
1256                        {
1257                                safestrncpy(email, auth_dat[i].email, sizeof(email));
1258                                expiration_time = (uint32)auth_dat[i].expiration_time;
1259                        }
1260
1261                        WFIFOHEAD(fd,50);
1262                        WFIFOW(fd,0) = 0x2717;
1263                        WFIFOL(fd,2) = account_id;
1264                        safestrncpy((char*)WFIFOP(fd,6), email, 40);
1265                        WFIFOL(fd,46) = expiration_time;
1266                        WFIFOSET(fd,50);
1267                }
1268                break;
1269
1270                case 0x2719: // ping request from charserver
1271                        if( RFIFOREST(fd) < 2 )
1272                                return 0;
1273                        RFIFOSKIP(fd,2);
1274
1275                        WFIFOHEAD(fd,2);
1276                        WFIFOW(fd,0) = 0x2718;
1277                        WFIFOSET(fd,2);
1278                break;
1279
1280                // Map server send information to change an email of an account via char-server
1281                case 0x2722:    // 0x2722 <account_id>.L <actual_e-mail>.40B <new_e-mail>.40B
1282                        if (RFIFOREST(fd) < 86)
1283                                return 0;
1284                {
1285                        char actual_email[40];
1286                        char new_email[40];
1287                        int account_id = RFIFOL(fd,2);
1288                        safestrncpy(actual_email, (char*)RFIFOP(fd,6), 40); remove_control_chars(actual_email);
1289                        safestrncpy(new_email, (char*)RFIFOP(fd,46), 40); remove_control_chars(new_email);
1290                        RFIFOSKIP(fd, 86);
1291
1292                        if( e_mail_check(actual_email) == 0 )
1293                                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);
1294                        else if( e_mail_check(new_email) == 0 )
1295                                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);
1296                        else if( strcmpi(new_email, "a@a.com") == 0 )
1297                                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);
1298                        else {
1299                                ARR_FIND( 0, auth_num, i, auth_dat[i].account_id == account_id );
1300                                if( i == auth_num )
1301                                        ShowNotice("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command), but account doesn't exist (account: %d, ip: %s).\n", server[id].name, account_id, ip);
1302                                else
1303                                if( strcmpi(auth_dat[i].email, actual_email) != 0 )
1304                                        ShowNotice("Char-server '%s': Attempt to modify an e-mail on an account (@email GM command), but actual e-mail is incorrect (account: %d (%s), actual e-mail: %s, proposed e-mail: %s, ip: %s).\n", server[id].name, account_id, auth_dat[i].userid, auth_dat[i].email, actual_email, ip);
1305                                else {
1306                                        safestrncpy(auth_dat[i].email, new_email, 40);
1307                                        ShowNotice("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, auth_dat[i].userid, new_email, ip);
1308                                        // Save
1309                                        mmo_auth_sync();
1310                                }
1311                        }
1312                }
1313                break;
1314
1315                case 0x2724: // Receiving an account state update request from a map-server (relayed via char-server)
1316                        if (RFIFOREST(fd) < 10)
1317                                return 0;
1318                {
1319                        int account_id = RFIFOL(fd,2);
1320                        uint32 state = RFIFOL(fd,6);
1321                        RFIFOSKIP(fd,10);
1322
1323                        ARR_FIND( 0, auth_num, i, auth_dat[i].account_id == account_id );
1324                        if( i == auth_num )
1325                                ShowNotice("Char-server '%s': Error of Status change (account: %d not found, suggested status %d, ip: %s).\n", server[id].name, account_id, state, ip);
1326                        else
1327                        if( auth_dat[i].state == state )
1328                                ShowNotice("Char-server '%s':  Error of Status change - actual status is already the good status (account: %d, status %d, ip: %s).\n", server[id].name, account_id, state, ip);
1329                        else {
1330                                ShowNotice("Char-server '%s': Status change (account: %d, new status %d, ip: %s).\n", server[id].name, account_id, state, ip);
1331                                if (state != 0) {
1332                                        uint8 buf[11];
1333                                        WBUFW(buf,0) = 0x2731;
1334                                        WBUFL(buf,2) = account_id;
1335                                        WBUFB(buf,6) = 0; // 0: change of state, 1: ban
1336                                        WBUFL(buf,7) = state; // status or final date of a banishment
1337                                        charif_sendallwos(-1, buf, 11);
1338                                }
1339                                auth_dat[i].state = state;
1340                                // Save
1341                                mmo_auth_sync();
1342                        }
1343                }
1344                break;
1345
1346                case 0x2725: // Receiving of map-server via char-server a ban request
1347                        if (RFIFOREST(fd) < 18)
1348                                return 0;
1349                {
1350                        int account_id = RFIFOL(fd,2);
1351                        int year = (short)RFIFOW(fd,6);
1352                        int month = (short)RFIFOW(fd,8);
1353                        int mday = (short)RFIFOW(fd,10);
1354                        int hour = (short)RFIFOW(fd,12);
1355                        int min = (short)RFIFOW(fd,14);
1356                        int sec = (short)RFIFOW(fd,16);
1357                        RFIFOSKIP(fd,18);
1358
1359                        ARR_FIND( 0, auth_num, i, auth_dat[i].account_id == account_id );
1360                        if( i == auth_num )
1361                                ShowNotice("Char-server '%s': Error of ban request (account: %d not found, ip: %s).\n", server[id].name, account_id, ip);
1362                        else
1363                        {
1364                                time_t timestamp;
1365                                struct tm *tmtime;
1366                                if (auth_dat[i].unban_time == 0 || auth_dat[i].unban_time < time(NULL))
1367                                        timestamp = time(NULL);
1368                                else
1369                                        timestamp = auth_dat[i].unban_time;
1370                                tmtime = localtime(&timestamp);
1371                                tmtime->tm_year = tmtime->tm_year + year;
1372                                tmtime->tm_mon = tmtime->tm_mon + month;
1373                                tmtime->tm_mday = tmtime->tm_mday + mday;
1374                                tmtime->tm_hour = tmtime->tm_hour + hour;
1375                                tmtime->tm_min = tmtime->tm_min + min;
1376                                tmtime->tm_sec = tmtime->tm_sec + sec;
1377                                timestamp = mktime(tmtime);
1378                                if (timestamp == -1)
1379                                        ShowNotice("Char-server '%s': Error of ban request (account: %d, invalid date, ip: %s).\n", server[id].name, account_id, ip);
1380                                else
1381                                if( timestamp <= time(NULL) || timestamp == 0 )
1382                                        ShowNotice("Char-server '%s': Error of ban request (account: %d, new date unbans the account, ip: %s).\n", server[id].name, account_id, ip);
1383                                else
1384                                {
1385                                        unsigned char buf[16];
1386                                        char tmpstr[2048];
1387                                        strftime(tmpstr, 24, login_config.date_format, localtime(&timestamp));
1388                                        ShowNotice("Char-server '%s': Ban request (account: %d, new final date of banishment: %d (%s), ip: %s).\n", server[id].name, account_id, timestamp, tmpstr, ip);
1389                                        WBUFW(buf,0) = 0x2731;
1390                                        WBUFL(buf,2) = auth_dat[i].account_id;
1391                                        WBUFB(buf,6) = 1; // 0: change of status, 1: ban
1392                                        WBUFL(buf,7) = (unsigned int)timestamp; // status or final date of a banishment
1393                                        charif_sendallwos(-1, buf, 11);
1394                                        auth_dat[i].unban_time = timestamp;
1395                                        // Save
1396                                        mmo_auth_sync();
1397                                }
1398                        }
1399                }
1400                break;
1401
1402                case 0x2727: // Change of sex (sex is reversed)
1403                        if( RFIFOREST(fd) < 6 )
1404                                return 0;
1405                {
1406                        int account_id = RFIFOL(fd,2);
1407                        RFIFOSKIP(fd,6);
1408
1409                        ARR_FIND( 0, auth_num, i, auth_dat[i].account_id == account_id );
1410                        if( i == auth_num )
1411                                ShowNotice("Char-server '%s': Error of sex change (account: %d not found, ip: %s).\n", server[id].name, account_id, ip);
1412                        else
1413                        if( auth_dat[i].sex == 'S' )
1414                                ShowNotice("Char-server '%s': Error of sex change - account to change is a Server account (account: %d, ip: %s).\n", server[id].name, account_id, ip);
1415                        else
1416                        {
1417                                unsigned char buf[7];
1418                                char sex = ( auth_dat[i].sex == 'M' ) ? 'F' : 'M'; //Change gender
1419
1420                                auth_dat[i].sex = sex;
1421
1422                                ShowNotice("Char-server '%s': Sex change (account: %d, new sex %c, ip: %s).\n", server[id].name, account_id, sex, ip);
1423                                WBUFW(buf,0) = 0x2723;
1424                                WBUFL(buf,2) = account_id;
1425                                WBUFB(buf,6) = sex_str2num(sex);
1426                                charif_sendallwos(-1, buf, 7);
1427                                // Save
1428                                mmo_auth_sync();
1429                        }
1430                }
1431                break;
1432
1433                case 0x2728:    // We receive account_reg2 from a char-server, and we send them to other map-servers.
1434                        if( RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2) )
1435                                return 0;
1436                {
1437                        int acc = RFIFOL(fd,4);
1438
1439                        ARR_FIND( 0, auth_num, i, auth_dat[i].account_id == acc );
1440                        if( i == auth_num )
1441                                ShowStatus("Char-server '%s': receiving (from the char-server) of account_reg2 (account: %d not found, ip: %s).\n", server[id].name, acc, ip);
1442                        else
1443                        {
1444                                int len;
1445                                int p;
1446                                ShowNotice("char-server '%s': receiving (from the char-server) of account_reg2 (account: %d, ip: %s).\n", server[id].name, acc, ip);
1447                                for(j=0,p=13;j<ACCOUNT_REG2_NUM && p<RFIFOW(fd,2);j++){
1448                                        sscanf((char*)RFIFOP(fd,p), "%31c%n",auth_dat[i].account_reg2[j].str,&len);
1449                                        auth_dat[i].account_reg2[j].str[len]='\0';
1450                                        p +=len+1; //+1 to skip the '\0' between strings.
1451                                        sscanf((char*)RFIFOP(fd,p), "%255c%n",auth_dat[i].account_reg2[j].value,&len);
1452                                        auth_dat[i].account_reg2[j].value[len]='\0';
1453                                        p +=len+1;
1454                                        remove_control_chars(auth_dat[i].account_reg2[j].str);
1455                                        remove_control_chars(auth_dat[i].account_reg2[j].value);
1456                                }
1457                                auth_dat[i].account_reg2_num = j;
1458
1459                                // Sending information towards the other char-servers.
1460                                RFIFOW(fd,0) = 0x2729;// reusing read buffer
1461                                charif_sendallwos(fd, RFIFOP(fd,0), RFIFOW(fd,2));
1462                                RFIFOSKIP(fd,RFIFOW(fd,2));
1463
1464                                // Save
1465                                mmo_auth_sync();
1466                        }
1467
1468                }
1469                break;
1470
1471                case 0x272a:    // Receiving of map-server via char-server an unban request
1472                        if( RFIFOREST(fd) < 6 )
1473                                return 0;
1474                {
1475                        int account_id = RFIFOL(fd,2);
1476                        RFIFOSKIP(fd,6);
1477
1478                        ARR_FIND( 0, auth_num, i, auth_dat[i].account_id == account_id );
1479                        if( i == auth_num )
1480                                ShowNotice("Char-server '%s': Error of UnBan request (account: %d not found, ip: %s).\n", server[id].name, account_id, ip);
1481                        else
1482                        if( auth_dat[i].unban_time == 0 )
1483                                ShowNotice("Char-server '%s': Error of UnBan request (account: %d, no change for unban date, ip: %s).\n", server[id].name, account_id, ip);
1484                        else
1485                        {
1486                                auth_dat[i].unban_time = 0;
1487                                ShowNotice("Char-server '%s': UnBan request (account: %d, ip: %s).\n", server[id].name, account_id, ip);
1488                        }
1489                }
1490                break;
1491
1492                case 0x272b:    // Set account_id to online [Wizputer]
1493                        if( RFIFOREST(fd) < 6 )
1494                                return 0;
1495                        add_online_user(id, RFIFOL(fd,2));
1496                        RFIFOSKIP(fd,6);
1497                break;
1498
1499                case 0x272c:   // Set account_id to offline [Wizputer]
1500                        if( RFIFOREST(fd) < 6 )
1501                                return 0;
1502                        remove_online_user(RFIFOL(fd,2));
1503                        RFIFOSKIP(fd,6);
1504                break;
1505
1506                case 0x272d:    // Receive list of all online accounts. [Skotlex]
1507                        if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2))
1508                                return 0;
1509                        if( login_config.online_check )
1510                        {
1511                                struct online_login_data *p;
1512                                int aid;
1513                                uint32 i, users;
1514                                online_db->foreach(online_db, online_db_setoffline, id); //Set all chars from this char-server offline first
1515                                users = RFIFOW(fd,4);
1516                                for (i = 0; i < users; i++) {
1517                                        aid = RFIFOL(fd,6+i*4);
1518                                        p = (struct online_login_data*)idb_ensure(online_db, aid, create_online_user);
1519                                        p->char_server = id;
1520                                        if (p->waiting_disconnect != -1)
1521                                        {
1522                                                delete_timer(p->waiting_disconnect, waiting_disconnect_timer);
1523                                                p->waiting_disconnect = -1;
1524                                        }
1525                                }
1526                        }
1527                        RFIFOSKIP(fd,RFIFOW(fd,2));
1528                break;
1529
1530                case 0x272e: //Request account_reg2 for a character.
1531                        if (RFIFOREST(fd) < 10)
1532                                return 0;
1533                {
1534                        size_t off;
1535
1536                        int account_id = RFIFOL(fd,2);
1537                        int char_id = RFIFOL(fd,6);
1538                        RFIFOSKIP(fd,10);
1539
1540                        WFIFOHEAD(fd,10000);
1541                        WFIFOW(fd,0) = 0x2729;
1542                        WFIFOL(fd,4) = account_id;
1543                        WFIFOL(fd,8) = char_id;
1544                        WFIFOB(fd,12) = 1; //Type 1 for Account2 registry
1545
1546                        ARR_FIND( 0, auth_num, i, auth_dat[i].account_id == account_id );
1547                        if( i == auth_num )
1548                        {
1549                                //Account not found? Send at least empty data, map servers need a reply!
1550                                WFIFOW(fd,2) = 13;
1551                                WFIFOSET(fd,WFIFOW(fd,2));
1552                                break;
1553                        }
1554
1555                        for( off = 13, j = 0; j < auth_dat[i].account_reg2_num && off < 9000; j++ )
1556                        {
1557                                if( auth_dat[i].account_reg2[j].str[0] != '\0' )
1558                                {
1559                                        off += sprintf((char*)WFIFOP(fd,off), "%s", auth_dat[i].account_reg2[j].str)+1; //We add 1 to consider the '\0' in place.
1560                                        off += sprintf((char*)WFIFOP(fd,off), "%s", auth_dat[i].account_reg2[j].value)+1;
1561                                }
1562                        }
1563
1564                        if( off >= 9000 )
1565                                ShowWarning("Too many account2 registries for AID %d. Some registries were not sent.\n", account_id);
1566
1567                        WFIFOW(fd,2) = (uint16)off;
1568                        WFIFOSET(fd,WFIFOW(fd,2));
1569                }
1570                break;
1571
1572                case 0x2736: // WAN IP update from char-server
1573                        if( RFIFOREST(fd) < 6 )
1574                                return 0;
1575                        server[id].ip = ntohl(RFIFOL(fd,2));
1576                        ShowInfo("Updated IP of Server #%d to %d.%d.%d.%d.\n",id, CONVIP(server[id].ip));
1577                        RFIFOSKIP(fd,6);
1578                break;
1579
1580                case 0x2737: //Request to set all offline.
1581                        ShowInfo("Setting accounts from char-server %d offline.\n", id);
1582                        online_db->foreach(online_db, online_db_setoffline, id);
1583                        RFIFOSKIP(fd,2);
1584                break;
1585
1586                default:
1587                        ShowError("parse_fromchar: Unknown packet 0x%x from a char-server! Disconnecting!\n", command);
1588                        set_eof(fd);
1589                        return 0;
1590                } // switch
1591        } // while
1592
1593        RFIFOSKIP(fd,RFIFOREST(fd));
1594        return 0;
1595}
1596
1597//--------------------------------------------
1598// Test to know if an IP come from LAN or WAN.
1599//--------------------------------------------
1600int lan_subnetcheck(uint32 ip)
1601{
1602        int i;
1603        ARR_FIND( 0, subnet_count, i, (subnet[i].char_ip & subnet[i].mask) == (ip & subnet[i].mask) );
1604        return ( i < subnet_count ) ? subnet[i].char_ip : 0;
1605}
1606
1607void login_auth_ok(struct login_session_data* sd)
1608{
1609        int fd = sd->fd;
1610        uint32 ip = session[fd]->client_addr;
1611
1612        uint8 server_num, n;
1613        uint32 subnet_char_ip;
1614        struct auth_node* node;
1615        int i;
1616
1617        sd->level = isGM(sd->account_id);
1618
1619        if( sd->level < login_config.min_level_to_connect )
1620        {
1621                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);
1622                WFIFOHEAD(fd,3);
1623                WFIFOW(fd,0) = 0x81;
1624                WFIFOB(fd,2) = 1; // 01 = Server closed
1625                WFIFOSET(fd,3);
1626                return;
1627        }
1628
1629        server_num = 0;
1630        for( i = 0; i < MAX_SERVERS; ++i )
1631                if( session_isValid(server[i].fd) )
1632                        server_num++;
1633
1634        if( server_num == 0 )
1635        {// if no char-server, don't send void list of servers, just disconnect the player with proper message
1636                ShowStatus("Connection refused: there is no char-server online (account: %s).\n", sd->userid);
1637                WFIFOHEAD(fd,3);
1638                WFIFOW(fd,0) = 0x81;
1639                WFIFOB(fd,2) = 1; // 01 = Server closed
1640                WFIFOSET(fd,3);
1641                return;
1642        }
1643
1644        if( login_config.online_check )
1645        {
1646                struct online_login_data* data = (struct online_login_data*)idb_get(online_db, sd->account_id);
1647                if( data )
1648                {// account is already marked as online!
1649                        if( data->char_server > -1 )
1650                        {// Request char servers to kick this account out. [Skotlex]
1651                                uint8 buf[6];
1652                                ShowNotice("User '%s' is already online - Rejected.\n", sd->userid);
1653                                WBUFW(buf,0) = 0x2734;
1654                                WBUFL(buf,2) = sd->account_id;
1655                                charif_sendallwos(-1, buf, 6);
1656                                if( data->waiting_disconnect == -1 )
1657                                        data->waiting_disconnect = add_timer(gettick()+AUTH_TIMEOUT, waiting_disconnect_timer, sd->account_id, 0);
1658
1659                                WFIFOHEAD(fd,3);
1660                                WFIFOW(fd,0) = 0x81;
1661                                WFIFOB(fd,2) = 8; // 08 = Server still recognizes your last login
1662                                WFIFOSET(fd,3);
1663                                return;
1664                        }
1665                        else
1666                        if( data->char_server == -1 )
1667                        {// client has authed but did not access char-server yet
1668                                // wipe previous session
1669                                idb_remove(auth_db, sd->account_id);
1670                                remove_online_user(sd->account_id);
1671                                data = NULL;
1672                        }
1673                }
1674        }
1675
1676        if( sd->level > 0 )
1677                ShowStatus("Connection of the GM (level:%d) account '%s' accepted.\n", sd->level, sd->userid);
1678        else
1679                ShowStatus("Connection of the account '%s' accepted.\n", sd->userid);
1680
1681        WFIFOHEAD(fd,47+32*server_num);
1682        WFIFOW(fd,0) = 0x69;
1683        WFIFOW(fd,2) = 47+32*server_num;
1684        WFIFOL(fd,4) = sd->login_id1;
1685        WFIFOL(fd,8) = sd->account_id;
1686        WFIFOL(fd,12) = sd->login_id2;
1687        WFIFOL(fd,16) = 0; // in old version, that was for ip (not more used)
1688        //memcpy(WFIFOP(fd,20), sd->lastlogin, 24); // in old version, that was for name (not more used)
1689        WFIFOW(fd,44) = 0; // unknown
1690        WFIFOB(fd,46) = sex_str2num(sd->sex);
1691        for( i = 0, n = 0; i < MAX_SERVERS; ++i )
1692        {
1693                if( !session_isValid(server[i].fd) )
1694                        continue;
1695
1696                subnet_char_ip = lan_subnetcheck(ip); // Advanced subnet check [LuzZza]
1697                WFIFOL(fd,47+n*32) = htonl((subnet_char_ip) ? subnet_char_ip : server[i].ip);
1698                WFIFOW(fd,47+n*32+4) = ntows(htons(server[i].port)); // [!] LE byte order here [!]
1699                memcpy(WFIFOP(fd,47+n*32+6), server[i].name, 20);
1700                WFIFOW(fd,47+n*32+26) = server[i].users;
1701                WFIFOW(fd,47+n*32+28) = server[i].maintenance;
1702                WFIFOW(fd,47+n*32+30) = server[i].new_;
1703                n++;
1704        }
1705        WFIFOSET(fd,47+32*server_num);
1706
1707        // create temporary auth entry
1708        CREATE(node, struct auth_node, 1);
1709        node->account_id = sd->account_id;
1710        node->login_id1 = sd->login_id1;
1711        node->login_id2 = sd->login_id2;
1712        node->sex = sd->sex;
1713        node->ip = ip;
1714        idb_put(auth_db, sd->account_id, node);
1715
1716        if( login_config.online_check )
1717        {
1718                struct online_login_data* data;
1719
1720                // mark client as 'online'
1721                data = add_online_user(-1, sd->account_id);
1722
1723                // schedule deletion of this node
1724                data->waiting_disconnect = add_timer(gettick()+AUTH_TIMEOUT, waiting_disconnect_timer, sd->account_id, 0);
1725        }
1726}
1727
1728void login_auth_failed(struct login_session_data* sd, int result)
1729{
1730        int fd = sd->fd;
1731
1732        WFIFOHEAD(fd,23);
1733        WFIFOW(fd,0) = 0x6a;
1734        WFIFOB(fd,2) = (uint8)result;
1735        if( result != 6 )
1736                memset(WFIFOP(fd,3), '\0', 20);
1737        else
1738        {// 6 = Your are Prohibited to log in until %s
1739                char tmpstr[20];
1740                int i = search_account_index(sd->userid);
1741                time_t unban_time = ( i >= 0 ) ? auth_dat[i].unban_time : 0;
1742                strftime(tmpstr, 20, login_config.date_format, localtime(&unban_time));
1743                safestrncpy((char*)WFIFOP(fd,3), tmpstr, 20); // ban timestamp goes here
1744        }
1745        WFIFOSET(fd,23);
1746}
1747
1748//----------------------------------------------------------------------------------------
1749// Default packet parsing (normal players or administation/char-server connection requests)
1750//----------------------------------------------------------------------------------------
1751int parse_login(int fd)
1752{
1753        struct login_session_data* sd = session[fd]->session_data;
1754        int result;
1755        uint32 ipl;
1756        char ip[16];
1757
1758        if( session[fd]->flag.eof )
1759        {
1760                do_close(fd);
1761                return 0;
1762        }
1763
1764        if( sd == NULL ) {
1765                sd = CREATE(session[fd]->session_data, struct login_session_data, 1);
1766                sd->fd = fd;
1767        }
1768
1769        ipl = session[fd]->client_addr;
1770        ip2str(ipl, ip);
1771
1772        while( RFIFOREST(fd) >= 2 )
1773        {
1774                uint16 command = RFIFOW(fd,0);
1775
1776                switch( command )
1777                {
1778
1779                case 0x0200:            // New alive packet: structure: 0x200 <account.userid>.24B. used to verify if client is always alive.
1780                        if (RFIFOREST(fd) < 26)
1781                                return 0;
1782                        RFIFOSKIP(fd,26);
1783                break;
1784
1785                case 0x0204:            // New alive packet: structure: 0x204 <encrypted.account.userid>.16B. (new ragexe from 22 june 2004)
1786                        if (RFIFOREST(fd) < 18)
1787                                return 0;
1788                        RFIFOSKIP(fd,18);
1789                break;
1790
1791                case 0x0064:            // request client login
1792                case 0x01dd:            // request client login (encryption mode)
1793                case 0x0277:            // New login packet (kRO 2006-04-24aSakexe langtype 0)
1794                case 0x02b0:            // New login packet (kRO 2007-05-14aSakexe langtype 0)
1795                {
1796                        size_t packet_len = RFIFOREST(fd); // assume no other packet was sent
1797
1798                        if( (command == 0x0064 && packet_len < 55)
1799                        ||  (command == 0x01dd && packet_len < 47)
1800                        ||  (command == 0x0277 && packet_len < 84)
1801                        ||  (command == 0x02b0 && packet_len < 85) )
1802                                return 0;
1803
1804                        // S 0064 <version>.l <account name>.24B <password>.24B <version2>.B
1805                        // S 01dd <version>.l <account name>.24B <md5 binary>.16B <version2>.B
1806                        // S 0277 <version>.l <account name>.24B <password>.24B <junk?>.29B <version2>.B
1807                        // S 02b0 <version>.l <account name>.24B <password>.24B <junk?>.30B <version2>.B
1808
1809                        sd->version = RFIFOL(fd,2);
1810                        safestrncpy(sd->userid, (char*)RFIFOP(fd,6), NAME_LENGTH); remove_control_chars(sd->userid);
1811                        if (command != 0x01dd) {
1812                                ShowStatus("Request for connection of %s (ip: %s).\n", sd->userid, ip);
1813                                safestrncpy(sd->passwd, (char*)RFIFOP(fd,30), NAME_LENGTH); remove_control_chars(sd->passwd);
1814                                sd->passwdenc = 0;
1815                        } else {
1816                                ShowStatus("Request for connection (encryption mode) of %s (ip: %s).\n", sd->userid, ip);
1817                                memcpy(sd->passwd, RFIFOP(fd,30), 16); sd->passwd[16] = '\0'; // raw binary data here!
1818                                sd->passwdenc = PASSWORDENC;
1819                        }
1820                        RFIFOSKIP(fd,packet_len);
1821
1822                        result = mmo_auth(sd);
1823
1824                        if( result == -1 )
1825                                login_auth_ok(sd);
1826                        else
1827                                login_auth_failed(sd, result);
1828                }
1829                break;
1830
1831                case 0x01db:    // Sending request of the coding key
1832                case 0x791a:    // Sending request of the coding key (administration packet)
1833                        RFIFOSKIP(fd,2);
1834                {
1835                        unsigned int i;
1836
1837                        memset(sd->md5key, '\0', sizeof(sd->md5key));
1838                        sd->md5keylen = (uint16)(12 + rand() % 4);
1839                        for( i = 0; i < sd->md5keylen; ++i )
1840                                sd->md5key[i] = (char)(1 + rand() % 255);
1841
1842                        WFIFOHEAD(fd,4 + sd->md5keylen);
1843                        WFIFOW(fd,0) = 0x01dc;
1844                        WFIFOW(fd,2) = 4 + sd->md5keylen;
1845                        memcpy(WFIFOP(fd,4), sd->md5key, sd->md5keylen);
1846                        WFIFOSET(fd,WFIFOW(fd,2));
1847                }
1848                break;
1849
1850                case 0x2710:    // Connection request of a char-server
1851                        if (RFIFOREST(fd) < 86)
1852                                return 0;
1853                {
1854                        char server_name[20];
1855                        uint32 server_ip;
1856                        uint16 server_port;
1857                        uint16 maintenance;
1858                        uint16 new_;
1859
1860                        safestrncpy(sd->userid, (char*)RFIFOP(fd,2), NAME_LENGTH); //remove_control_chars(account.userid);
1861                        safestrncpy(sd->passwd, (char*)RFIFOP(fd,26), NAME_LENGTH); //remove_control_chars(account.passwd);
1862                        sd->passwdenc = 0;
1863                        sd->version = login_config.client_version_to_connect; // hack to skip version check
1864
1865                        server_ip = ntohl(RFIFOL(fd,54));
1866                        server_port = ntohs(RFIFOW(fd,58));
1867
1868                        safestrncpy(server_name, (char*)RFIFOP(fd,60), 20); remove_control_chars(server_name);
1869                        maintenance = RFIFOW(fd,82);
1870                        new_ = RFIFOW(fd,84);
1871                        RFIFOSKIP(fd,86);
1872
1873                        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);
1874
1875                        result = mmo_auth(sd);
1876                        if( result == -1 && sd->sex == 'S' && sd->account_id < MAX_SERVERS && server[sd->account_id].fd == -1 )
1877                        {
1878                                ShowStatus("Connection of the char-server '%s' accepted.\n", server_name);
1879                                safestrncpy(server[sd->account_id].name, server_name, sizeof(server[sd->account_id].name));
1880                                server[sd->account_id].fd = fd;
1881                                server[sd->account_id].ip = server_ip;
1882                                server[sd->account_id].port = server_port;
1883                                server[sd->account_id].users = 0;
1884                                server[sd->account_id].maintenance = maintenance;
1885                                server[sd->account_id].new_ = new_;
1886
1887                                session[fd]->func_parse = parse_fromchar;
1888                                session[fd]->flag.server = 1;
1889                                realloc_fifo(fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK);
1890
1891                                // send connection success
1892                                WFIFOHEAD(fd,3);
1893                                WFIFOW(fd,0) = 0x2711;
1894                                WFIFOB(fd,2) = 0;
1895                                WFIFOSET(fd,3);
1896
1897                                // send GM account to char-server
1898                                send_GM_accounts(fd);
1899                        }
1900                        else
1901                        {
1902                                ShowNotice("Connection of the char-server '%s' REFUSED.\n", server_name);
1903                                WFIFOHEAD(fd,3);
1904                                WFIFOW(fd,0) = 0x2711;
1905                                WFIFOB(fd,2) = 3;
1906                                WFIFOSET(fd,3);
1907                        }
1908                }
1909                return 0; // processing will continue elsewhere
1910
1911                case 0x7530:    // Server version information request
1912                        ShowStatus("Sending server version information to ip: %s\n", ip);
1913                        RFIFOSKIP(fd,2);
1914                        WFIFOHEAD(fd,10);
1915                        WFIFOW(fd,0) = 0x7531;
1916                        WFIFOB(fd,2) = ATHENA_MAJOR_VERSION;
1917                        WFIFOB(fd,3) = ATHENA_MINOR_VERSION;
1918                        WFIFOB(fd,4) = ATHENA_REVISION;
1919                        WFIFOB(fd,5) = ATHENA_RELEASE_FLAG;
1920                        WFIFOB(fd,6) = ATHENA_OFFICIAL_FLAG;
1921                        WFIFOB(fd,7) = ATHENA_SERVER_LOGIN;
1922                        WFIFOW(fd,8) = ATHENA_MOD_VERSION;
1923                        WFIFOSET(fd,10);
1924                break;
1925
1926                case 0x7918:    // Request for administation login
1927                        if ((int)RFIFOREST(fd) < 4 || (int)RFIFOREST(fd) < ((RFIFOW(fd,2) == 0) ? 28 : 20))
1928                                return 0;
1929                        WFIFOW(fd,0) = 0x7919;
1930                        WFIFOB(fd,2) = 1;
1931                        if( session[fd]->client_addr != admin_allowed_ip ) {
1932                                ShowNotice("'ladmin'-login: Connection in administration mode refused: IP isn't authorised (ladmin_allow, ip: %s).\n", ip);
1933                        } else {
1934                                struct login_session_data *ld = (struct login_session_data*)session[fd]->session_data;
1935                                if (RFIFOW(fd,2) == 0) {        // non encrypted password
1936                                        char password[25];
1937                                        memcpy(password, RFIFOP(fd,4), 24);
1938                                        password[24] = '\0';
1939                                        remove_control_chars(password);
1940                                        if( !admin_state )
1941                                                ShowNotice("'ladmin'-login: Connection in administration mode REFUSED - remote administration is disabled (non encrypted password: %s, ip: %s)\n", password, ip);
1942                                        else
1943                                        if( strcmp(password, admin_pass) != 0)
1944                                                ShowNotice("'ladmin'-login: Connection in administration mode REFUSED - invalid password (non encrypted password: %s, ip: %s)\n", password, ip);
1945                                        else {
1946                                                // If remote administration is enabled and password sent by client matches password read from login server configuration file
1947                                                ShowNotice("'ladmin'-login: Connection in administration mode accepted (non encrypted password: %s, ip: %s)\n", password, ip);
1948                                                WFIFOB(fd,2) = 0;
1949                                                session[fd]->func_parse = parse_admin;
1950                                        }
1951                                } else {        // encrypted password
1952                                        if (!ld)
1953                                                ShowError("'ladmin'-login: error! MD5 key not created/requested for an administration login.\n");
1954                                        else {
1955                                                char md5str[64] = "", md5bin[32];
1956                                                if (RFIFOW(fd,2) == 1) {
1957                                                        sprintf(md5str, "%s%s", ld->md5key, admin_pass); // 20 24
1958                                                } else if (RFIFOW(fd,2) == 2) {
1959                                                        sprintf(md5str, "%s%s", admin_pass, ld->md5key); // 24 20
1960                                                }
1961                                                MD5_String2binary(md5str, md5bin);
1962                                                if( !admin_state )
1963                                                        ShowNotice("'ladmin'-login: Connection in administration mode REFUSED - remote administration is disabled (encrypted password, ip: %s)\n", ip);
1964                                                else
1965                                                if( memcmp(md5bin, RFIFOP(fd,4), 16) != 0 )
1966                                                        ShowNotice("'ladmin'-login: Connection in administration mode REFUSED - invalid password (encrypted password, ip: %s)\n", ip);
1967                                                else {
1968                                                        // If remote administration is enabled and password hash sent by client matches hash of password read from login server configuration file
1969                                                        ShowNotice("'ladmin'-login: Connection in administration mode accepted (encrypted password, ip: %s)\n", ip);
1970                                                        ShowNotice("Connection of a remote administration accepted (encrypted password).\n");
1971                                                        WFIFOB(fd,2) = 0;
1972                                                        session[fd]->func_parse = parse_admin;
1973                                                }
1974                                        }
1975                                }
1976                        }
1977                        WFIFOSET(fd,3);
1978
1979                        RFIFOSKIP(fd, (RFIFOW(fd,2) == 0) ? 28 : 20);
1980                break;
1981
1982                default:
1983                        ShowNotice("Abnormal end of connection (ip: %s): Unknown packet 0x%x\n", ip, command);
1984                        set_eof(fd);
1985                        return 0;
1986                }
1987        }
1988
1989        RFIFOSKIP(fd,RFIFOREST(fd));
1990        return 0;
1991}
1992
1993//-----------------------
1994// Console Command Parser [Wizputer]
1995//-----------------------
1996int parse_console(char* buf)
1997{
1998        char command[256];
1999
2000        memset(command, 0, sizeof(command));
2001
2002        sscanf(buf, "%[^\n]", command);
2003
2004        ShowInfo("Console command :%s", command);
2005
2006        if( strcmpi("shutdown", command) == 0 ||
2007            strcmpi("exit", command) == 0 ||
2008            strcmpi("quit", command) == 0 ||
2009            strcmpi("end", command) == 0 )
2010                runflag = 0;
2011        else
2012        if( strcmpi("alive", command) == 0 ||
2013            strcmpi("status", command) == 0 )
2014                ShowInfo(CL_CYAN"Console: "CL_BOLD"I'm Alive."CL_RESET"\n");
2015        else
2016        if( strcmpi("help", command) == 0 ) {
2017                ShowInfo(CL_BOLD"Help of commands:"CL_RESET"\n");
2018                ShowInfo("  To shutdown the server:\n");
2019                ShowInfo("  'shutdown|exit|quit|end'\n");
2020                ShowInfo("  To know if server is alive:\n");
2021                ShowInfo("  'alive|status'\n");
2022        }
2023
2024        return 0;
2025}
2026
2027static int online_data_cleanup_sub(DBKey key, void *data, va_list ap)
2028{
2029        struct online_login_data *character= (struct online_login_data*)data;
2030        if (character->char_server == -2) //Unknown server.. set them offline
2031                remove_online_user(character->account_id);
2032        return 0;
2033}
2034
2035static int online_data_cleanup(int tid, unsigned int tick, int id, intptr data)
2036{
2037        online_db->foreach(online_db, online_data_cleanup_sub);
2038        return 0;
2039} 
2040
2041//----------------------------------
2042// Reading Lan Support configuration
2043//----------------------------------
2044int login_lan_config_read(const char *lancfgName)
2045{
2046        FILE *fp;
2047        int line_num = 0;
2048        char line[1024], w1[64], w2[64], w3[64], w4[64];
2049
2050        if((fp = fopen(lancfgName, "r")) == NULL) {
2051                ShowWarning("LAN Support configuration file is not found: %s\n", lancfgName);
2052                return 1;
2053        }
2054
2055        ShowInfo("Reading the configuration file %s...\n", lancfgName);
2056
2057        while(fgets(line, sizeof(line), fp))
2058        {
2059                line_num++;
2060                if ((line[0] == '/' && line[1] == '/') || line[0] == '\n' || line[1] == '\n')
2061                        continue;
2062
2063                if(sscanf(line,"%[^:]: %[^:]:%[^:]:%[^\r\n]", w1, w2, w3, w4) != 4)
2064                {
2065                        ShowWarning("Error syntax of configuration file %s in line %d.\n", lancfgName, line_num);
2066                        continue;
2067                }
2068
2069                if( strcmpi(w1, "subnet") == 0 )
2070                {
2071                        subnet[subnet_count].mask = str2ip(w2);
2072                        subnet[subnet_count].char_ip = str2ip(w3);
2073                        subnet[subnet_count].map_ip = str2ip(w4);
2074
2075                        if( (subnet[subnet_count].char_ip & subnet[subnet_count].mask) != (subnet[subnet_count].map_ip & subnet[subnet_count].mask) )
2076                        {
2077                                ShowError("%s: Configuration Error: The char server (%s) and map server (%s) belong to different subnetworks!\n", lancfgName, w3, w4);
2078                                continue;
2079                        }
2080
2081                        subnet_count++;
2082                }
2083        }
2084
2085        ShowStatus("Read information about %d subnetworks.\n", subnet_count);
2086
2087        fclose(fp);
2088        return 0;
2089}
2090
2091//-----------------------------------
2092// Reading main configuration file
2093//-----------------------------------
2094int login_config_read(const char* cfgName)
2095{
2096        char line[1024], w1[1024], w2[1024];
2097        FILE* fp = fopen(cfgName, "r");
2098        if (fp == NULL) {
2099                ShowError("Configuration file (%s) not found.\n", cfgName);
2100                return 1;
2101        }
2102        ShowInfo("Reading configuration file %s...\n", cfgName);
2103        while(fgets(line, sizeof(line), fp))
2104        {
2105                if (line[0] == '/' && line[1] == '/')
2106                        continue;
2107
2108                if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) < 2)
2109                        continue;
2110
2111                if(!strcmpi(w1,"timestamp_format"))
2112                        strncpy(timestamp_format, w2, 20);
2113                else if(!strcmpi(w1,"stdout_with_ansisequence"))
2114                        stdout_with_ansisequence = config_switch(w2);
2115                else if(!strcmpi(w1,"console_silent")) {
2116                        ShowInfo("Console Silent Setting: %d\n", atoi(w2));
2117                        msg_silent = atoi(w2);
2118                }
2119                else if( !strcmpi(w1, "bind_ip") ) {
2120                        char ip_str[16];
2121                        login_config.login_ip = host2ip(w2);
2122                        if( login_config.login_ip )
2123                                ShowStatus("Login server binding IP address : %s -> %s\n", w2, ip2str(login_config.login_ip, ip_str));
2124                }
2125                else if( !strcmpi(w1, "login_port") ) {
2126                        login_config.login_port = (uint16)atoi(w2);
2127                        ShowStatus("set login_port : %s\n",w2);
2128                }
2129                else if(!strcmpi(w1, "log_login"))
2130                        login_config.log_login = (bool)config_switch(w2);
2131
2132                else if (strcmpi(w1, "admin_state") == 0) {
2133                        admin_state = (bool)config_switch(w2);
2134                } else if (strcmpi(w1, "admin_pass") == 0) {
2135                        memset(admin_pass, 0, sizeof(admin_pass));
2136                        strncpy(admin_pass, w2, sizeof(admin_pass));
2137                        admin_pass[sizeof(admin_pass)-1] = '\0';
2138                } else if (strcmpi(w1, "admin_allowed_ip") == 0)
2139                        admin_allowed_ip = host2ip(w2);
2140                else if (strcmpi(w1, "account_filename") == 0) {
2141                        memset(account_filename, 0, sizeof(account_filename));
2142                        strncpy(account_filename, w2, sizeof(account_filename));
2143                        account_filename[sizeof(account_filename)-1] = '\0';
2144                } else if (strcmpi(w1, "gm_account_filename") == 0) {
2145                        memset(GM_account_filename, 0, sizeof(GM_account_filename));
2146                        strncpy(GM_account_filename, w2, sizeof(GM_account_filename));
2147                        GM_account_filename[sizeof(GM_account_filename)-1] = '\0';
2148                }
2149                else if (strcmpi(w1, "gm_account_filename_check_timer") == 0)
2150                        gm_account_filename_check_timer = atoi(w2);
2151
2152                else if(!strcmpi(w1, "new_account"))
2153                        login_config.new_account_flag = (bool)config_switch(w2);
2154                else if(!strcmpi(w1, "start_limited_time"))
2155                        login_config.start_limited_time = atoi(w2);
2156                else if(!strcmpi(w1, "check_client_version"))
2157                        login_config.check_client_version = (bool)config_switch(w2);
2158                else if(!strcmpi(w1, "client_version_to_connect"))
2159                        login_config.client_version_to_connect = atoi(w2);
2160                else if(!strcmpi(w1, "use_MD5_passwords"))
2161                        login_config.use_md5_passwds = (bool)config_switch(w2);
2162                else if(!strcmpi(w1, "min_level_to_connect"))
2163                        login_config.min_level_to_connect = atoi(w2);
2164                else if(!strcmpi(w1, "date_format"))
2165                        safestrncpy(login_config.date_format, w2, sizeof(login_config.date_format));
2166                else if(!strcmpi(w1, "console"))
2167                        login_config.console = (bool)config_switch(w2);
2168//              else if(!strcmpi(w1, "case_sensitive"))
2169//                      login_config.case_sensitive = config_switch(w2);
2170                else if(!strcmpi(w1, "allowed_regs")) //account flood protection system
2171                        allowed_regs = atoi(w2);
2172                else if(!strcmpi(w1, "time_allowed"))
2173                        time_allowed = atoi(w2);
2174                else if(!strcmpi(w1, "online_check"))
2175                        login_config.online_check = (bool)config_switch(w2);
2176                else if(!strcmpi(w1, "use_dnsbl"))
2177                        login_config.use_dnsbl = (bool)config_switch(w2);
2178                else if(!strcmpi(w1, "dnsbl_servers"))
2179                        safestrncpy(login_config.dnsbl_servs, w2, sizeof(login_config.dnsbl_servs));
2180                else if(!strcmpi(w1, "ip_sync_interval"))
2181                        login_config.ip_sync_interval = (unsigned int)1000*60*atoi(w2); //w2 comes in minutes.
2182                else if(!strcmpi(w1, "import"))
2183                        login_config_read(w2);
2184        }
2185        fclose(fp);
2186        ShowInfo("Finished reading %s.\n", cfgName);
2187        return 0;
2188}
2189
2190//-------------------------------------
2191// Displaying of configuration warnings
2192//-------------------------------------
2193void display_conf_warnings(void)
2194{
2195        if( admin_state ) {
2196                if (admin_pass[0] == '\0') {
2197                        ShowWarning("Administrator password is void (admin_pass).\n");
2198                } else if (strcmp(admin_pass, "admin") == 0) {
2199                        ShowWarning("You are using the default administrator password (admin_pass).\n");
2200                        ShowWarning("  We highly recommend that you change it.\n");
2201                }
2202        }
2203
2204        if (gm_account_filename_check_timer < 0) {
2205                ShowWarning("Invalid value for gm_account_filename_check_timer parameter. Setting to 15 sec (default).\n");
2206                gm_account_filename_check_timer = 15;
2207        } else if (gm_account_filename_check_timer == 1) {
2208                ShowWarning("Invalid value for gm_account_filename_check_timer parameter. Setting to 2 sec (minimum value).\n");
2209                gm_account_filename_check_timer = 2;
2210        }
2211
2212        if (login_config.min_level_to_connect < 0) { // 0: all players, 1-99 at least gm level x
2213                ShowWarning("Invalid value for min_level_to_connect (%d) parameter -> setting 0 (any player).\n", login_config.min_level_to_connect);
2214                login_config.min_level_to_connect = 0;
2215        } else if (login_config.min_level_to_connect > 99) { // 0: all players, 1-99 at least gm level x
2216                ShowWarning("Invalid value for min_level_to_connect (%d) parameter -> setting to 99 (only GM level 99)\n", login_config.min_level_to_connect);
2217                login_config.min_level_to_connect = 99;
2218        }
2219
2220        if (login_config.start_limited_time < -1) { // -1: create unlimited account, 0 or more: additionnal sec from now to create limited time
2221                ShowWarning("Invalid value for start_limited_time parameter\n");
2222                ShowWarning("  -> setting to -1 (new accounts are created with unlimited time).\n");
2223                login_config.start_limited_time = -1;
2224        }
2225
2226        return;
2227}
2228
2229void login_set_defaults()
2230{
2231        login_config.login_ip = INADDR_ANY;
2232        login_config.login_port = 6900;
2233        login_config.ip_sync_interval = 0;
2234        login_config.log_login = true;
2235        safestrncpy(login_config.date_format, "%Y-%m-%d %H:%M:%S", sizeof(login_config.date_format));
2236        login_config.console = false;
2237        login_config.new_account_flag = true;
2238//      login_config.case_sensitive = true;
2239        login_config.use_md5_passwds = false;
2240//      login_config.login_gm_read = true;
2241        login_config.min_level_to_connect = 0;
2242        login_config.online_check = true;
2243        login_config.check_client_version = false;
2244        login_config.client_version_to_connect = 20;
2245
2246//      login_config.ipban = true;
2247//      login_config.dynamic_pass_failure_ban = true;
2248//      login_config.dynamic_pass_failure_ban_interval = 5;
2249//      login_config.dynamic_pass_failure_ban_limit = 7;
2250//      login_config.dynamic_pass_failure_ban_duration = 5;
2251        login_config.use_dnsbl = false;
2252        safestrncpy(login_config.dnsbl_servs, "", sizeof(login_config.dnsbl_servs));
2253}
2254
2255//--------------------------------------
2256// Function called at exit of the server
2257//--------------------------------------
2258void do_final(void)
2259{
2260        int i, fd;
2261        ShowStatus("Terminating...\n");
2262
2263        mmo_auth_sync();
2264        online_db->destroy(online_db, NULL);
2265        auth_db->destroy(auth_db, NULL);
2266
2267        if(auth_dat) aFree(auth_dat);
2268        if(gm_account_db) aFree(gm_account_db);
2269
2270        for (i = 0; i < MAX_SERVERS; i++) {
2271                if ((fd = server[i].fd) >= 0) {
2272                        memset(&server[i], 0, sizeof(struct mmo_char_server));
2273                        server[i].fd = -1;
2274                        do_close(fd);
2275                }
2276        }
2277        do_close(login_fd);
2278
2279        ShowStatus("Finished.\n");
2280}
2281
2282//------------------------------
2283// Function called when the server
2284// has received a crash signal.
2285//------------------------------
2286void do_abort(void)
2287{
2288}
2289
2290void set_server_type(void)
2291{
2292        SERVER_TYPE = ATHENA_SERVER_LOGIN;
2293}
2294
2295//------------------------------
2296// Login server initialization
2297//------------------------------
2298int do_init(int argc, char** argv)
2299{
2300        int i;
2301
2302        login_set_defaults();
2303
2304        // read login-server configuration
2305        login_config_read((argc > 1) ? argv[1] : LOGIN_CONF_NAME);
2306        display_conf_warnings(); // not in login_config_read, because we can use 'import' option, and display same message twice or more
2307        login_lan_config_read((argc > 2) ? argv[2] : LAN_CONF_NAME);
2308
2309        srand((unsigned int)time(NULL));
2310
2311        for( i = 0; i < MAX_SERVERS; i++ )
2312                server[i].fd = -1;
2313
2314        // Accounts database init
2315        mmo_auth_init();
2316
2317        // Online user database init
2318        online_db = idb_alloc(DB_OPT_RELEASE_DATA);
2319        add_timer_func_list(waiting_disconnect_timer, "waiting_disconnect_timer");
2320
2321        // Interserver auth init
2322        auth_db = idb_alloc(DB_OPT_RELEASE_DATA);
2323
2324        // Read account information.
2325        read_gm_account();
2326
2327        // set default parser as parse_login function
2328        set_defaultparse(parse_login);
2329
2330        add_timer_func_list(check_auth_sync, "check_auth_sync");
2331        add_timer_interval(gettick() + 60000, check_auth_sync, 0, 0, 60000); // every 60 sec we check if we must save accounts file (only if necessary to save)
2332
2333        // every x sec we check if gm file has been changed
2334        if( gm_account_filename_check_timer ) {
2335                add_timer_func_list(check_GM_file, "check_GM_file");
2336                add_timer_interval(gettick() + gm_account_filename_check_timer * 1000, check_GM_file, 0, 0, gm_account_filename_check_timer * 1000); 
2337        }
2338
2339        // every 10 minutes cleanup online account db.
2340        add_timer_func_list(online_data_cleanup, "online_data_cleanup");
2341        add_timer_interval(gettick() + 600*1000, online_data_cleanup, 0, 0, 600*1000);
2342
2343        // add timer to detect ip address change and perform update
2344        if (login_config.ip_sync_interval) {
2345                add_timer_func_list(sync_ip_addresses, "sync_ip_addresses");
2346                add_timer_interval(gettick() + login_config.ip_sync_interval, sync_ip_addresses, 0, 0, login_config.ip_sync_interval);
2347        }
2348
2349        if( login_config.console )
2350        {
2351                //##TODO invoke a CONSOLE_START plugin event
2352        }
2353
2354        new_reg_tick = gettick();
2355
2356        // server port open & binding
2357        login_fd = make_listen_bind(login_config.login_ip, login_config.login_port);
2358
2359        ShowStatus("The login-server is "CL_GREEN"ready"CL_RESET" (Server is listening on the port %u).\n\n", login_config.login_port);
2360
2361        return 0;
2362}
Note: See TracBrowser for help on using the browser.