summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorshennetsind <ind@henn.et>2013-03-09 00:04:28 -0300
committershennetsind <ind@henn.et>2013-03-09 00:04:28 -0300
commitd2d734ce0983fbed72e69f555f57f29de04f30b3 (patch)
treea3332e1eea882a9df5a2253d9a63153a9197851a
parentbb0f807d0683fcb2c0e9fdd6a5d1e54686dfc816 (diff)
downloadhercules-d2d734ce0983fbed72e69f555f57f29de04f30b3.tar.gz
hercules-d2d734ce0983fbed72e69f555f57f29de04f30b3.tar.bz2
hercules-d2d734ce0983fbed72e69f555f57f29de04f30b3.tar.xz
hercules-d2d734ce0983fbed72e69f555f57f29de04f30b3.zip
Hercules Renewal'd Pin Code
Feature is not, I repeat, NOT complete. the decryption is not fully functional which leads to dial values different from the ones the player used. Credits: lemongrass3110 for the base yommy for the packets LightFighter for the decrypt function (altho its not stable :P) Signed-off-by: shennetsind <ind@henn.et>
-rw-r--r--conf/char-server.conf21
-rw-r--r--sql-files/main.sql6
-rw-r--r--sql-files/upgrades/2013-03-06--00-00.sql (renamed from sql-files/upgrades/upgrade_svn17178.sql)4
-rw-r--r--sql-files/upgrades/2013-03-09--01-56.sql4
-rw-r--r--sql-files/upgrades/index.txt4
-rw-r--r--src/char/CMakeLists.txt2
-rw-r--r--src/char/Makefile.in4
-rw-r--r--src/char/char.c106
-rw-r--r--src/char/char.h23
-rw-r--r--src/char/pincode.c191
-rw-r--r--src/char/pincode.h41
-rw-r--r--src/login/account.h26
-rw-r--r--src/login/account_sql.c14
-rw-r--r--src/login/login.c47
-rw-r--r--vcproj-10/char-server.vcxproj2
-rw-r--r--vcproj-10/char-server.vcxproj.filters6
-rw-r--r--vcproj-12/char-server.vcxproj2
-rw-r--r--vcproj-12/char-server.vcxproj.filters6
-rw-r--r--vcproj-9/char-server.vcproj8
19 files changed, 456 insertions, 61 deletions
diff --git a/conf/char-server.conf b/conf/char-server.conf
index 4ed7c1a26..26503f468 100644
--- a/conf/char-server.conf
+++ b/conf/char-server.conf
@@ -159,4 +159,25 @@ char_del_delay: 86400
// What folder the DB files are in (item_db.txt, etc.)
db_path: db
+//==================================================================
+// Pincode system -- INCOMPLETE / BROKEN
+//==================================================================
+
+// A window is opened before you can select your character and you will have to enter a pincode by using only your mouse
+// NOTE: Requires client 2011-03-09aragexeRE or newer.
+// 0: disabled
+// 1: enabled
+pincode_enabled: 0
+
+// How often does a user have to change his pincode?
+// Default: 0
+// 0: never
+// X: every X minutes
+pincode_changetime: 0
+
+// How often can a user enter the wrong password?
+// Default: 3
+// NOTE: The maximum on clientside is 3
+pincode_maxtry: 3
+
import: conf/import/char_conf.txt
diff --git a/sql-files/main.sql b/sql-files/main.sql
index 3270bf7e2..b7b3e5226 100644
--- a/sql-files/main.sql
+++ b/sql-files/main.sql
@@ -438,7 +438,9 @@ CREATE TABLE IF NOT EXISTS `login` (
`lastlogin` datetime NOT NULL default '0000-00-00 00:00:00',
`last_ip` varchar(100) NOT NULL default '',
`birthdate` DATE NOT NULL DEFAULT '0000-00-00',
- `character_slots` TINYINT( 3 ) unsigned NOT NULL,
+ `character_slots` TINYINT( 3 ) unsigned NOT NULL default '0',
+ `pincode` varchar(4) NOT NULL default '',
+ `pincode_change` int(11) unsigned NOT NULL default '0',
PRIMARY KEY (`account_id`),
KEY `name` (`userid`)
) ENGINE=MyISAM AUTO_INCREMENT=2000000;
@@ -648,6 +650,8 @@ CREATE TABLE IF NOT EXISTS `sql_updates` (
INSERT INTO `sql_updates` (`timestamp`) VALUES (1360858500);
INSERT INTO `sql_updates` (`timestamp`) VALUES (1360951560);
INSERT INTO `sql_updates` (`timestamp`) VALUES (1362445531);
+INSERT INTO `sql_updates` (`timestamp`) VALUES (1362528000);
+INSERT INTO `sql_updates` (`timestamp`) VALUES (1362794218);
--
-- Table structure for table `sstatus`
diff --git a/sql-files/upgrades/upgrade_svn17178.sql b/sql-files/upgrades/2013-03-06--00-00.sql
index 02c4c1475..844e3d8a8 100644
--- a/sql-files/upgrades/upgrade_svn17178.sql
+++ b/sql-files/upgrades/2013-03-06--00-00.sql
@@ -1,7 +1,9 @@
--- This script resets all quests that were done by your users before this revision
+#1362528000
+-- This script resets all dewata quests that were done by your users before this revision
-- Author: Euphy
DELETE FROM `quest` WHERE `quest_id` > 5034 AND `quest_id` < 5055;
DELETE FROM `quest` WHERE `quest_id` > 9154 AND `quest_id` < 9166;
DELETE FROM `global_reg_value` WHERE `str` = 'dewata_gatti';
DELETE FROM `global_reg_value` WHERE `str` = 'dewata_legend';
DELETE FROM `global_reg_value` WHERE `str` = 'dewata_oldman';
+INSERT INTO `sql_updates` (`timestamp`) VALUES (1362528000); \ No newline at end of file
diff --git a/sql-files/upgrades/2013-03-09--01-56.sql b/sql-files/upgrades/2013-03-09--01-56.sql
new file mode 100644
index 000000000..43811ee33
--- /dev/null
+++ b/sql-files/upgrades/2013-03-09--01-56.sql
@@ -0,0 +1,4 @@
+#1362794218
+ALTER TABLE `login` ADD COLUMN `pincode` varchar(4) NOT NULL DEFAULT '';
+ALTER TABLE `login` ADD COLUMN `pincode_change` int(11) unsigned NOT NULL DEFAULT '0';
+INSERT INTO `sql_updates` (`timestamp`) VALUES (1362794218); \ No newline at end of file
diff --git a/sql-files/upgrades/index.txt b/sql-files/upgrades/index.txt
index bd835414c..a803adffc 100644
--- a/sql-files/upgrades/index.txt
+++ b/sql-files/upgrades/index.txt
@@ -1,3 +1,5 @@
2013-02-14--16-15.sql
2013-02-15--18-06.sql
-2013-03-05--01-05.sql \ No newline at end of file
+2013-03-05--01-05.sql
+2013-03-06--00-00.sql
+2013-03-09--01-56.sql \ No newline at end of file
diff --git a/src/char/CMakeLists.txt b/src/char/CMakeLists.txt
index 8c71a3b4c..9413ff303 100644
--- a/src/char/CMakeLists.txt
+++ b/src/char/CMakeLists.txt
@@ -23,6 +23,7 @@ set( SQL_CHAR_HEADERS
"${CMAKE_CURRENT_SOURCE_DIR}/int_quest.h"
"${CMAKE_CURRENT_SOURCE_DIR}/int_storage.h"
"${CMAKE_CURRENT_SOURCE_DIR}/inter.h"
+ "${CMAKE_CURRENT_SOURCE_DIR}/pincode.h"
)
set( SQL_CHAR_SOURCES
"${CMAKE_CURRENT_SOURCE_DIR}/char.c"
@@ -37,6 +38,7 @@ set( SQL_CHAR_SOURCES
"${CMAKE_CURRENT_SOURCE_DIR}/int_quest.c"
"${CMAKE_CURRENT_SOURCE_DIR}/int_storage.c"
"${CMAKE_CURRENT_SOURCE_DIR}/inter.c"
+ "${CMAKE_CURRENT_SOURCE_DIR}/pincode.c"
)
set( DEPENDENCIES common_sql )
set( LIBRARIES ${GLOBAL_LIBRARIES} )
diff --git a/src/char/Makefile.in b/src/char/Makefile.in
index a64145dea..a320579b5 100644
--- a/src/char/Makefile.in
+++ b/src/char/Makefile.in
@@ -16,8 +16,8 @@ COMMON_SQL_OBJ = ../common/obj_sql/sql.o
COMMON_H = ../common/sql.h
CHAR_OBJ = obj_sql/char.o obj_sql/inter.o obj_sql/int_party.o obj_sql/int_guild.o \
- obj_sql/int_storage.o obj_sql/int_pet.o obj_sql/int_homun.o obj_sql/int_mail.o obj_sql/int_auction.o obj_sql/int_quest.o obj_sql/int_mercenary.o obj_sql/int_elemental.o
-CHAR_H = char.h inter.h int_party.h int_guild.h int_storage.h int_pet.h int_homun.h int_mail.h int_auction.h int_quest.h int_mercenary.h int_elemental.h
+ obj_sql/int_storage.o obj_sql/int_pet.o obj_sql/int_homun.o obj_sql/int_mail.o obj_sql/int_auction.o obj_sql/int_quest.o obj_sql/int_mercenary.o obj_sql/int_elemental.o obj_sql/pincode.o
+CHAR_H = char.h inter.h int_party.h int_guild.h int_storage.h int_pet.h int_homun.h int_mail.h int_auction.h int_quest.h int_mercenary.h int_elemental.h pincode.h
HAVE_MYSQL=@HAVE_MYSQL@
ifeq ($(HAVE_MYSQL),yes)
diff --git a/src/char/char.c b/src/char/char.c
index aed678684..4223fdded 100644
--- a/src/char/char.c
+++ b/src/char/char.c
@@ -21,6 +21,7 @@
#include "int_storage.h"
#include "char.h"
#include "inter.h"
+#include "pincode.h"
#include <sys/types.h>
#include <time.h>
@@ -83,7 +84,7 @@ struct mmo_map_server {
unsigned short map[MAX_MAP_PER_SERVER];
} server[MAX_MAP_SERVERS];
-int login_fd=-1, char_fd=-1;
+int char_fd=-1;
char userid[24];
char passwd[24];
char server_name[20];
@@ -121,20 +122,6 @@ struct s_subnet {
} subnet[16];
int subnet_count = 0;
-struct char_session_data {
- bool auth; // whether the session is authed or not
- int account_id, login_id1, login_id2, sex;
- int found_char[MAX_CHARS]; // ids of chars on this account
- char email[40]; // e-mail (default: a@a.com) by [Yor]
- time_t expiration_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited)
- int group_id; // permission
- uint8 char_slots;
- uint32 version;
- uint8 clienttype;
- char new_name[NAME_LENGTH];
- char birthdate[10+1]; // YYYY-MM-DD
-};
-
int max_connect_user = -1;
int gm_allow_group = -1;
int autosave_interval = DEFAULT_AUTOSAVE_INTERVAL;
@@ -2149,13 +2136,12 @@ int parse_fromlogin(int fd) {
break;
case 0x2717: // account data
- if (RFIFOREST(fd) < 63)
+ if (RFIFOREST(fd) < 72)
return 0;
// find the authenticated session with this account id
ARR_FIND( 0, fd_max, i, session[i] && (sd = (struct char_session_data*)session[i]->session_data) && sd->auth && sd->account_id == RFIFOL(fd,2) );
- if( i < fd_max )
- {
+ if( i < fd_max ) {
int server_id;
memcpy(sd->email, RFIFOP(fd,6), 40);
sd->expiration_time = (time_t)RFIFOL(fd,46);
@@ -2167,6 +2153,8 @@ int parse_fromlogin(int fd) {
} else if ( !sd->char_slots )/* no value aka 0 in sql */
sd->char_slots = MAX_CHARS;/* cap to maximum */
safestrncpy(sd->birthdate, (const char*)RFIFOP(fd,52), sizeof(sd->birthdate));
+ safestrncpy(sd->pincode, (const char*)RFIFOP(fd,63), sizeof(sd->pincode));
+ sd->pincode_change = RFIFOL(fd,68);
ARR_FIND( 0, ARRAYLENGTH(server), server_id, server[server_id].fd > 0 && server[server_id].map[0] );
// continued from char_auth_ok...
if( server_id == ARRAYLENGTH(server) || //server not online, bugreport:2359
@@ -2180,19 +2168,12 @@ int parse_fromlogin(int fd) {
} else {
// send characters to player
mmo_char_send006b(i, sd);
-#if PACKETVER >= 20110309
- // PIN code system, disabled
- WFIFOHEAD(i, 12);
- WFIFOW(i, 0) = 0x08B9;
- WFIFOW(i, 2) = 0;
- WFIFOW(i, 4) = 0;
- WFIFOL(i, 6) = sd->account_id;
- WFIFOW(i, 10) = 0;
- WFIFOSET(i, 12);
+#if PACKETVER >= 20110309
+ pincode->handle(i, sd);
#endif
}
}
- RFIFOSKIP(fd,63);
+ RFIFOSKIP(fd,72);
break;
// login-server alive packet
@@ -4190,6 +4171,50 @@ int parse_char(int fd)
}
return 0; // avoid processing of followup packets here
+ // checks the entered pin
+ case 0x8b8:
+ if( RFIFOREST(fd) < 10 )
+ return 0;
+
+ if( RFIFOL(fd,2) == sd->account_id )
+ pincode->check( fd, sd );
+
+ RFIFOSKIP(fd,10);
+ break;
+
+ // request for PIN window
+ case 0x8c5:
+ if( RFIFOREST(fd) < 6 )
+ return 0;
+
+ if( RFIFOL(fd,2) == sd->account_id )
+ pincode->state( fd, sd, PINCODE_NOTSET );
+
+ RFIFOSKIP(fd,6);
+ break;
+
+ // pincode change request
+ case 0x8be:
+ if( RFIFOREST(fd) < 14 )
+ return 0;
+
+ if( RFIFOL(fd,2) == sd->account_id )
+ pincode->change( fd, sd );
+
+ RFIFOSKIP(fd,14);
+ break;
+
+ // activate PIN system and set first PIN
+ case 0x8ba:
+ if( RFIFOREST(fd) < 10 )
+ return 0;
+
+ if( RFIFOL(fd,2) == sd->account_id )
+ pincode->new( fd, sd );
+
+ RFIFOSKIP(fd,10);
+ break;
+
// unknown packet received
default:
ShowError("parse_char: Received unknown packet "CL_WHITE"0x%x"CL_RESET" from ip '"CL_WHITE"%s"CL_RESET"'! Disconnecting!\n", RFIFOW(fd,0), ip2str(ipl, NULL));
@@ -4544,7 +4569,17 @@ void sql_config_read(const char* cfgName)
fclose(fp);
ShowInfo("Done reading %s.\n", cfgName);
}
-
+void char_config_dispatch(char *w1, char *w2) {
+ bool (*dispatch_to[]) (char *w1, char *w2) = {
+ /* as many as it needs */
+ pincode->config_read
+ };
+ int i, len = ARRAYLENGTH(dispatch_to);
+ for(i = 0; i < len; i++) {
+ if( (*dispatch_to[i])(w1,w2) )
+ break;/* we found who this belongs to, can stop */
+ }
+}
int char_config_read(const char* cfgName)
{
char line[1024], w1[1024], w2[1024];
@@ -4695,7 +4730,8 @@ int char_config_read(const char* cfgName)
guild_exp_rate = atoi(w2);
} else if (strcmpi(w1, "import") == 0) {
char_config_read(w2);
- }
+ } else
+ char_config_dispatch(w1,w2);
}
fclose(fp);
@@ -4774,6 +4810,8 @@ int do_init(int argc, char **argv)
mapindex_init();
start_point.map = mapindex_name2id("new_zone01");
+ pincode_defaults();
+
char_config_read((argc < 2) ? CHAR_CONF_NAME : argv[1]);
char_lan_config_read((argc > 3) ? argv[3] : LAN_CONF_NAME);
sql_config_read(SQL_CONF_NAME);
@@ -4783,7 +4821,7 @@ int do_init(int argc, char **argv)
ShowNotice("Please edit your 'login' table to create a proper inter-server user/password (gender 'S')\n");
ShowNotice("And then change the user/password to use in conf/char_athena.conf (or conf/import/char_conf.txt)\n");
}
-
+
inter_init_sql((argc > 2) ? argv[2] : inter_cfgName); // inter server configuration
auth_db = idb_alloc(DB_OPT_RELEASE_DATA);
@@ -4791,8 +4829,7 @@ int do_init(int argc, char **argv)
mmo_char_sql_init();
char_read_fame_list(); //Read fame lists.
- if ((naddr_ != 0) && (!login_ip || !char_ip))
- {
+ if ((naddr_ != 0) && (!login_ip || !char_ip)) {
char ip_str[16];
ip2str(addr_[0], ip_str);
@@ -4824,8 +4861,7 @@ int do_init(int argc, char **argv)
add_timer_func_list(online_data_cleanup, "online_data_cleanup");
add_timer_interval(gettick() + 1000, online_data_cleanup, 0, 0, 600 * 1000);
- if( console )
- {
+ if( console ) {
//##TODO invoke a CONSOLE_START plugin event
}
diff --git a/src/char/char.h b/src/char/char.h
index e16350cb3..d75dc21f9 100644
--- a/src/char/char.h
+++ b/src/char/char.h
@@ -7,8 +7,7 @@
#include "../config/core.h"
#include "../common/core.h" // CORE_ST_LAST
-enum E_CHARSERVER_ST
-{
+enum E_CHARSERVER_ST {
CHARSERVER_ST_RUNNING = CORE_ST_LAST,
CHARSERVER_ST_SHUTDOWN,
CHARSERVER_ST_LAST
@@ -16,6 +15,24 @@ enum E_CHARSERVER_ST
struct mmo_charstatus;
+struct char_session_data {
+ bool auth; // whether the session is authed or not
+ int account_id, login_id1, login_id2, sex;
+ int found_char[MAX_CHARS]; // ids of chars on this account
+ char email[40]; // e-mail (default: a@a.com) by [Yor]
+ time_t expiration_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited)
+ int group_id; // permission
+ uint8 char_slots;
+ uint32 version;
+ uint8 clienttype;
+ char pincode[4+1];
+ uint16 pincode_seed;
+ uint16 pincode_try;
+ uint32 pincode_change;
+ char new_name[NAME_LENGTH];
+ char birthdate[10+1]; // YYYY-MM-DD
+};
+
#define MAX_MAP_SERVERS 30
#define DEFAULT_AUTOSAVE_INTERVAL 300*1000
@@ -39,7 +56,7 @@ int char_family(int pl1,int pl2,int pl3);
int request_accreg2(int account_id, int char_id);
int save_accreg2(unsigned char* buf, int len);
-
+int login_fd;
extern int char_name_option;
extern char char_name_letters[];
extern bool char_gm_read;
diff --git a/src/char/pincode.c b/src/char/pincode.c
new file mode 100644
index 000000000..610f9b12f
--- /dev/null
+++ b/src/char/pincode.c
@@ -0,0 +1,191 @@
+// Copyright (c) Hercules Dev Team, licensed under GNU GPL.
+// See the LICENSE file
+
+#include "../common/cbasetypes.h"
+#include "../common/mmo.h"
+#include "../common/random.h"
+#include "../common/showmsg.h"
+#include "../common/socket.h"
+#include "../common/strlib.h"
+#include "char.h"
+#include "pincode.h"
+
+#include <stdlib.h>
+
+int enabled = PINCODE_OK;
+int changetime = 0;
+int maxtry = 3;
+unsigned long multiplier = 0x3498, baseSeed = 0x881234;
+
+void pincode_handle ( int fd, struct char_session_data* sd ) {
+ if( pincode->enabled ){
+ // PIN code system enabled
+ if( sd->pincode[0] == '\0' ){
+ // No PIN code has been set yet
+ pincode->state( fd, sd, PINCODE_NOTSET );
+ } else {
+ if( !pincode->changetime || !( time(NULL) > (sd->pincode_change+*pincode->changetime) ) ){
+ // Ask user for his PIN code
+ pincode->state( fd, sd, PINCODE_ASK );
+ } else {
+ // User hasnt changed his PIN code too long
+ pincode->state( fd, sd, PINCODE_EXPIRED );
+ }
+ }
+ } else {
+ // PIN code system, disabled
+ pincode->state( fd, sd, PINCODE_OK );
+ }
+}
+
+void pincode_check(int fd, struct char_session_data* sd) {
+ char pin[5];
+
+ safestrncpy((char*)pin, (char*)RFIFOP(fd, 6), 4+1);
+
+ pincode->decrypt(sd->pincode_seed, pin);
+
+ if( pincode->compare( fd, sd, pin ) ){
+ pincode->state( fd, sd, PINCODE_OK );
+ }
+}
+
+int pincode_compare(int fd, struct char_session_data* sd, char* pin) {
+ if( strcmp( sd->pincode, pin ) == 0 ){
+ sd->pincode_try = 0;
+ return 1;
+ } else {
+ pincode->state( fd, sd, PINCODE_WRONG );
+
+ if( pincode->maxtry && ++sd->pincode_try >= *pincode->maxtry ){
+ pincode->error( sd->account_id );
+ }
+
+ return 0;
+ }
+}
+
+void pincode_change(int fd, struct char_session_data* sd) {
+ char oldpin[5], newpin[5];
+
+ safestrncpy(oldpin, (char*)RFIFOP(fd,6), 4+1);
+ pincode->decrypt(sd->pincode_seed,oldpin);
+ if( !pincode->compare( fd, sd, oldpin ) )
+ return;
+
+ safestrncpy(newpin, (char*)RFIFOP(fd,10), 4+1);
+ pincode->decrypt(sd->pincode_seed,newpin);
+ pincode->update( sd->account_id, newpin );
+
+ pincode->state( fd, sd, PINCODE_OK );
+}
+
+void pincode_setnew(int fd, struct char_session_data* sd) {
+ char newpin[5];
+
+ safestrncpy(newpin, (char*)RFIFOP(fd,6), 4+1);
+ pincode->decrypt(sd->pincode_seed,newpin);
+
+ pincode->update( sd->account_id, newpin );
+
+ pincode->state( fd, sd, PINCODE_OK );
+}
+
+// 0 = disabled / pin is correct
+// 1 = ask for pin - client sends 0x8b8
+// 2 = create new pin - client sends 0x8ba
+// 3 = pin must be changed - client 0x8be
+// 4 = create new pin ?? - client sends 0x8ba
+// 5 = client shows msgstr(1896)
+// 6 = client shows msgstr(1897) Unable to use your KSSN number
+// 7 = char select window shows a button - client sends 0x8c5
+// 8 = pincode was incorrect
+void pincode_sendstate(int fd, struct char_session_data* sd, uint16 state) {
+ WFIFOHEAD(fd, 12);
+ WFIFOW(fd, 0) = 0x8b9;
+ WFIFOL(fd, 2) = sd->pincode_seed = rnd() % 0xFFFF;
+ WFIFOL(fd, 6) = sd->account_id;
+ WFIFOW(fd,10) = state;
+ WFIFOSET(fd,12);
+}
+
+void pincode_notifyLoginPinUpdate(int account_id, char* pin) {
+ WFIFOHEAD(login_fd,11);
+ WFIFOW(login_fd,0) = 0x2738;
+ WFIFOL(login_fd,2) = account_id;
+ strncpy( (char*)WFIFOP(login_fd,6), pin, 5 );
+ WFIFOSET(login_fd,11);
+}
+
+void pincode_notifyLoginPinError(int account_id) {
+ WFIFOHEAD(login_fd,6);
+ WFIFOW(login_fd,0) = 0x2739;
+ WFIFOL(login_fd,2) = account_id;
+ WFIFOSET(login_fd,6);
+}
+
+void pincode_decrypt(unsigned long userSeed, char* pin) {
+ int i, pos;
+ char tab[10] = {0,1,2,3,4,5,6,7,8,9};
+
+ for( i = 1; i < 10; i++ ){
+ userSeed = *pincode->baseSeed + userSeed * *pincode->multiplier;
+ pos = userSeed % (i + 1);
+ if( i != pos ){
+ tab[i] ^= tab[pos];
+ tab[pos] ^= tab[i];
+ tab[i] ^= tab[pos];
+ }
+ }
+
+ for( i = 0; i < 4; i++ ){
+ pin[i] = tab[pin[i]- '0'];
+ }
+
+ sprintf(pin, "%d%d%d%d", pin[0], pin[1], pin[2], pin[3]);
+}
+
+bool pincode_config_read(char *w1, char *w2) {
+
+ while ( true ) {
+
+ if ( strcmpi(w1, "pincode_enabled") == 0 ) {
+ enabled = atoi(w2);
+ } else if ( strcmpi(w1, "pincode_changetime") == 0 ) {
+ changetime = atoi(w2)*60;
+ } else if ( strcmpi(w1, "pincode_maxtry") == 0 ) {
+ maxtry = atoi(w2);
+ if( maxtry > 3 ) {
+ ShowWarning("pincode_maxtry is too high (%d); maximum allowed: 3! capping to 3...\n",maxtry);
+ maxtry = 3;
+ }
+ } else
+ return false;
+
+ break;
+ }
+
+ return true;
+}
+
+void pincode_defaults(void) {
+ pincode = &pincode_s;
+
+ pincode->enabled = &enabled;
+ pincode->changetime = &changetime;
+ pincode->maxtry = &maxtry;
+ pincode->multiplier = &multiplier;
+ pincode->baseSeed = &baseSeed;
+
+ pincode->handle = pincode_handle;
+ pincode->decrypt = pincode_decrypt;
+ pincode->error = pincode_notifyLoginPinError;
+ pincode->update = pincode_notifyLoginPinUpdate;
+ pincode->state = pincode_sendstate;
+ pincode->new = pincode_setnew;
+ pincode->change = pincode_change;
+ pincode->compare = pincode_compare;
+ pincode->check = pincode_check;
+ pincode->config_read = pincode_config_read;
+
+} \ No newline at end of file
diff --git a/src/char/pincode.h b/src/char/pincode.h
new file mode 100644
index 000000000..209817c78
--- /dev/null
+++ b/src/char/pincode.h
@@ -0,0 +1,41 @@
+// Copyright (c) Hercules Dev Team, licensed under GNU GPL.
+// See the LICENSE file
+
+#ifndef _PINCODE_H_
+#define _PINCODE_H_
+
+#include "char.h"
+
+#define PINCODE_OK 0
+#define PINCODE_ASK 1
+#define PINCODE_NOTSET 2
+#define PINCODE_EXPIRED 3
+#define PINCODE_UNUSED 7
+#define PINCODE_WRONG 8
+
+/* Pincode Interface */
+struct pincode_interface {
+ /* vars */
+ int *enabled;
+ int *changetime;
+ int *maxtry;
+ unsigned long *multiplier;
+ unsigned long *baseSeed;
+ /* handler */
+ void (*handle) (int fd, struct char_session_data* sd);
+ void (*decrypt) (unsigned long userSeed, char* pin);
+ void (*error) (int account_id);
+ void (*update) (int account_id, char* pin);
+ void (*state) (int fd, struct char_session_data* sd, uint16 state);
+ void (*new) (int fd, struct char_session_data* sd);
+ void (*change) (int fd, struct char_session_data* sd);
+ int (*compare) (int fd, struct char_session_data* sd, char* pin);
+ void (*check) (int fd, struct char_session_data* sd);
+ bool (*config_read) (char *w1, char *w2);
+} pincode_s;
+
+struct pincode_interface *pincode;
+
+void pincode_defaults(void);
+
+#endif /* _PINCODE_H_ */ \ No newline at end of file
diff --git a/src/login/account.h b/src/login/account.h
index 56708d5e9..be5bad988 100644
--- a/src/login/account.h
+++ b/src/login/account.h
@@ -38,18 +38,20 @@ struct mmo_account
{
int account_id;
char userid[NAME_LENGTH];
- char pass[32+1]; // 23+1 for plaintext, 32+1 for md5-ed passwords
- char sex; // gender (M/F/S)
- char email[40]; // e-mail (by default: a@a.com)
- int group_id; // player group id
- uint8 char_slots; // this accounts maximum character slots (maximum is limited to MAX_CHARS define in char server)
- unsigned int state; // packet 0x006a value + 1 (0: compte OK)
- time_t unban_time; // (timestamp): ban time limit of the account (0 = no ban)
- time_t expiration_time; // (timestamp): validity limit of the account (0 = unlimited)
- unsigned int logincount;// number of successful auth attempts
- char lastlogin[24]; // date+time of last successful login
- char last_ip[16]; // save of last IP of connection
- char birthdate[10+1]; // assigned birth date (format: YYYY-MM-DD, default: 0000-00-00)
+ char pass[32+1]; // 23+1 for plaintext, 32+1 for md5-ed passwords
+ char sex; // gender (M/F/S)
+ char email[40]; // e-mail (by default: a@a.com)
+ int group_id; // player group id
+ uint8 char_slots; // this accounts maximum character slots (maximum is limited to MAX_CHARS define in char server)
+ unsigned int state; // packet 0x006a value + 1 (0: compte OK)
+ time_t unban_time; // (timestamp): ban time limit of the account (0 = no ban)
+ time_t expiration_time; // (timestamp): validity limit of the account (0 = unlimited)
+ unsigned int logincount; // number of successful auth attempts
+ unsigned int pincode_change;// (timestamp): last time of pincode change
+ char pincode[4+1]; // pincode value
+ char lastlogin[24]; // date+time of last successful login
+ char last_ip[16]; // save of last IP of connection
+ char birthdate[10+1]; // assigned birth date (format: YYYY-MM-DD, default: 0000-00-00)
int account_reg2_num;
struct global_reg account_reg2[ACCOUNT_REG2_NUM]; // account script variables (stored on login server)
};
diff --git a/src/login/account_sql.c b/src/login/account_sql.c
index f8c39efc9..6f21cbb6c 100644
--- a/src/login/account_sql.c
+++ b/src/login/account_sql.c
@@ -522,7 +522,7 @@ static bool mmo_auth_fromsql(AccountDB_SQL* db, struct mmo_account* acc, int acc
// retrieve login entry for the specified account
if( SQL_ERROR == Sql_Query(sql_handle,
- "SELECT `account_id`,`userid`,`user_pass`,`sex`,`email`,`group_id`,`state`,`unban_time`,`expiration_time`,`logincount`,`lastlogin`,`last_ip`,`birthdate`,`character_slots` FROM `%s` WHERE `account_id` = %d",
+ "SELECT `account_id`,`userid`,`user_pass`,`sex`,`email`,`group_id`,`state`,`unban_time`,`expiration_time`,`logincount`,`lastlogin`,`last_ip`,`birthdate`,`character_slots`,`pincode`,`pincode_change` FROM `%s` WHERE `account_id` = %d",
db->account_db, account_id )
) {
Sql_ShowDebug(sql_handle);
@@ -549,7 +549,9 @@ static bool mmo_auth_fromsql(AccountDB_SQL* db, struct mmo_account* acc, int acc
Sql_GetData(sql_handle, 11, &data, NULL); safestrncpy(acc->last_ip, data, sizeof(acc->last_ip));
Sql_GetData(sql_handle, 12, &data, NULL); safestrncpy(acc->birthdate, data, sizeof(acc->birthdate));
Sql_GetData(sql_handle, 13, &data, NULL); acc->char_slots = atoi(data);
-
+ Sql_GetData(sql_handle, 14, &data, NULL); safestrncpy(acc->pincode, data, sizeof(acc->pincode));
+ Sql_GetData(sql_handle, 15, &data, NULL); acc->pincode_change = atol(data);
+
Sql_FreeResult(sql_handle);
@@ -597,7 +599,7 @@ static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, boo
if( is_new )
{// insert into account table
if( SQL_SUCCESS != SqlStmt_Prepare(stmt,
- "INSERT INTO `%s` (`account_id`, `userid`, `user_pass`, `sex`, `email`, `group_id`, `state`, `unban_time`, `expiration_time`, `logincount`, `lastlogin`, `last_ip`, `birthdate`, `character_slots`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
+ "INSERT INTO `%s` (`account_id`, `userid`, `user_pass`, `sex`, `email`, `group_id`, `state`, `unban_time`, `expiration_time`, `logincount`, `lastlogin`, `last_ip`, `birthdate`, `character_slots`, `pincode`, `pincode_change`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
db->account_db)
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_INT, (void*)&acc->account_id, sizeof(acc->account_id))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_STRING, (void*)acc->userid, strlen(acc->userid))
@@ -613,6 +615,8 @@ static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, boo
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 11, SQLDT_STRING, (void*)&acc->last_ip, strlen(acc->last_ip))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 12, SQLDT_STRING, (void*)&acc->birthdate, strlen(acc->birthdate))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 13, SQLDT_UCHAR, (void*)&acc->char_slots, sizeof(acc->char_slots))
+ || SQL_SUCCESS != SqlStmt_BindParam(stmt, 14, SQLDT_STRING, (void*)&acc->pincode, sizeof(acc->pincode))
+ || SQL_SUCCESS != SqlStmt_BindParam(stmt, 15, SQLDT_LONG, (void*)&acc->pincode_change, sizeof(acc->pincode_change))
|| SQL_SUCCESS != SqlStmt_Execute(stmt)
) {
SqlStmt_ShowDebug(stmt);
@@ -621,7 +625,7 @@ static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, boo
}
else
{// update account table
- if( SQL_SUCCESS != SqlStmt_Prepare(stmt, "UPDATE `%s` SET `userid`=?,`user_pass`=?,`sex`=?,`email`=?,`group_id`=?,`state`=?,`unban_time`=?,`expiration_time`=?,`logincount`=?,`lastlogin`=?,`last_ip`=?,`birthdate`=?,`character_slots`=? WHERE `account_id` = '%d'", db->account_db, acc->account_id)
+ if( SQL_SUCCESS != SqlStmt_Prepare(stmt, "UPDATE `%s` SET `userid`=?,`user_pass`=?,`sex`=?,`email`=?,`group_id`=?,`state`=?,`unban_time`=?,`expiration_time`=?,`logincount`=?,`lastlogin`=?,`last_ip`=?,`birthdate`=?,`character_slots`=?,`pincode`=?,`pincode_change`=? WHERE `account_id` = '%d'", db->account_db, acc->account_id)
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_STRING, (void*)acc->userid, strlen(acc->userid))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_STRING, (void*)acc->pass, strlen(acc->pass))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 2, SQLDT_ENUM, (void*)&acc->sex, sizeof(acc->sex))
@@ -635,6 +639,8 @@ static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, boo
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 10, SQLDT_STRING, (void*)&acc->last_ip, strlen(acc->last_ip))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 11, SQLDT_STRING, (void*)&acc->birthdate, strlen(acc->birthdate))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 12, SQLDT_UCHAR, (void*)&acc->char_slots, sizeof(acc->char_slots))
+ || SQL_SUCCESS != SqlStmt_BindParam(stmt, 13, SQLDT_STRING, (void*)&acc->pincode, strlen(acc->pincode))
+ || SQL_SUCCESS != SqlStmt_BindParam(stmt, 14, SQLDT_LONG, (void*)&acc->pincode_change, sizeof(acc->pincode_change))
|| SQL_SUCCESS != SqlStmt_Execute(stmt)
) {
SqlStmt_ShowDebug(stmt);
diff --git a/src/login/login.c b/src/login/login.c
index e174672cc..c16cce342 100644
--- a/src/login/login.c
+++ b/src/login/login.c
@@ -559,6 +559,7 @@ int parse_fromchar(int fd)
int group_id = 0;
uint8 char_slots = 0;
char birthdate[10+1] = "";
+ char pincode[4+1] = "\0\0\0\0\0";
int account_id = RFIFOL(fd,2);
RFIFOSKIP(fd,6);
@@ -570,10 +571,13 @@ int parse_fromchar(int fd)
expiration_time = acc.expiration_time;
group_id = acc.group_id;
char_slots = acc.char_slots;
+ safestrncpy(pincode, acc.pincode, sizeof(pincode));
safestrncpy(birthdate, acc.birthdate, sizeof(birthdate));
+ if( strlen(pincode) == 0 )
+ memset(pincode,'\0',sizeof(pincode));
}
- WFIFOHEAD(fd,63);
+ WFIFOHEAD(fd,72);
WFIFOW(fd,0) = 0x2717;
WFIFOL(fd,2) = account_id;
safestrncpy((char*)WFIFOP(fd,6), email, 40);
@@ -581,7 +585,9 @@ int parse_fromchar(int fd)
WFIFOB(fd,50) = (unsigned char)group_id;
WFIFOB(fd,51) = char_slots;
safestrncpy((char*)WFIFOP(fd,52), birthdate, 10+1);
- WFIFOSET(fd,63);
+ safestrncpy((char*)WFIFOP(fd,63), pincode, 4+1 );
+ WFIFOL(fd,68) = acc.pincode_change;
+ WFIFOSET(fd,72);
}
break;
@@ -907,6 +913,41 @@ int parse_fromchar(int fd)
RFIFOSKIP(fd,2);
break;
+ case 0x2738: //Change PIN Code for a account
+ if( RFIFOREST(fd) < 11 )
+ return 0;
+ else {
+ struct mmo_account acc;
+
+ if( accounts->load_num(accounts, &acc, RFIFOL(fd,2) ) ) {
+ strncpy( acc.pincode, (char*)RFIFOP(fd,6), 5 );
+ acc.pincode_change = time( NULL );
+ accounts->save(accounts, &acc);
+ }
+ RFIFOSKIP(fd,11);
+ }
+ break;
+
+ case 0x2739: // PIN Code was entered wrong too often
+ if( RFIFOREST(fd) < 6 )
+ return 0;
+ else {
+ struct mmo_account acc;
+
+ if( accounts->load_num(accounts, &acc, RFIFOL(fd,2) ) ) {
+ struct online_login_data* ld;
+
+ if( ( ld = (struct online_login_data*)idb_get(online_db,acc.account_id) ) == NULL )
+ return 0;
+
+ login_log( host2ip(acc.last_ip), acc.userid, 100, "PIN Code check failed" );
+ }
+
+ remove_online_user(acc.account_id);
+ RFIFOSKIP(fd,6);
+ }
+ break;
+
default:
ShowError("parse_fromchar: Unknown packet 0x%x from a char-server! Disconnecting!\n", command);
set_eof(fd);
@@ -958,6 +999,8 @@ int mmo_auth_new(const char* userid, const char* pass, const char sex, const cha
safestrncpy(acc.lastlogin, "0000-00-00 00:00:00", sizeof(acc.lastlogin));
safestrncpy(acc.last_ip, last_ip, sizeof(acc.last_ip));
safestrncpy(acc.birthdate, "0000-00-00", sizeof(acc.birthdate));
+ safestrncpy(acc.pincode, "\0", sizeof(acc.pincode));
+ acc.pincode_change = 0;
acc.char_slots = 0;
if( !accounts->create(accounts, &acc) )
diff --git a/vcproj-10/char-server.vcxproj b/vcproj-10/char-server.vcxproj
index 898f0cb68..6b72a6ca7 100644
--- a/vcproj-10/char-server.vcxproj
+++ b/vcproj-10/char-server.vcxproj
@@ -168,6 +168,7 @@
<ClInclude Include="..\src\char\int_quest.h" />
<ClInclude Include="..\src\char\int_storage.h" />
<ClInclude Include="..\src\char\inter.h" />
+ <ClInclude Include="..\src\char\pincode.h" />
<ClInclude Include="..\src\common\winapi.h" />
</ItemGroup>
<ItemGroup>
@@ -206,6 +207,7 @@
<ClCompile Include="..\src\char\int_quest.c" />
<ClCompile Include="..\src\char\int_storage.c" />
<ClCompile Include="..\src\char\inter.c" />
+ <ClCompile Include="..\src\char\pincode.c" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
diff --git a/vcproj-10/char-server.vcxproj.filters b/vcproj-10/char-server.vcxproj.filters
index 06f69e35e..3a0fd40ab 100644
--- a/vcproj-10/char-server.vcxproj.filters
+++ b/vcproj-10/char-server.vcxproj.filters
@@ -76,6 +76,9 @@
<ClCompile Include="..\src\char\inter.c">
<Filter>char_sql</Filter>
</ClCompile>
+ ClCompile Include="..\src\char\pincode.c">
+ <Filter>char_sql</Filter>
+ </ClCompile>
<ClCompile Include="..\3rdparty\mt19937ar\mt19937ar.c">
<Filter>3rdparty\mt19937ar</Filter>
</ClCompile>
@@ -156,6 +159,9 @@
<ClInclude Include="..\src\char\inter.h">
<Filter>char_sql</Filter>
</ClInclude>
+ <ClInclude Include="..\src\char\pincode.h">
+ <Filter>char_sql</Filter>
+ </ClInclude>
<ClInclude Include="..\src\char\char.h">
<Filter>char_sql</Filter>
</ClInclude>
diff --git a/vcproj-12/char-server.vcxproj b/vcproj-12/char-server.vcxproj
index bd7f5c4bd..aa8cb34c8 100644
--- a/vcproj-12/char-server.vcxproj
+++ b/vcproj-12/char-server.vcxproj
@@ -171,6 +171,7 @@
<ClInclude Include="..\src\char\int_quest.h" />
<ClInclude Include="..\src\char\int_storage.h" />
<ClInclude Include="..\src\char\inter.h" />
+ <ClInclude Include="..\src\char\pincode.h" />
<ClInclude Include="..\src\common\winapi.h" />
</ItemGroup>
<ItemGroup>
@@ -209,6 +210,7 @@
<ClCompile Include="..\src\char\int_quest.c" />
<ClCompile Include="..\src\char\int_storage.c" />
<ClCompile Include="..\src\char\inter.c" />
+ <ClCompile Include="..\src\char\pincode.c" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
diff --git a/vcproj-12/char-server.vcxproj.filters b/vcproj-12/char-server.vcxproj.filters
index 06f69e35e..d78e5e70d 100644
--- a/vcproj-12/char-server.vcxproj.filters
+++ b/vcproj-12/char-server.vcxproj.filters
@@ -76,6 +76,9 @@
<ClCompile Include="..\src\char\inter.c">
<Filter>char_sql</Filter>
</ClCompile>
+ <ClCompile Include="..\src\char\pincode.c">
+ <Filter>char_sql</Filter>
+ </ClCompile>
<ClCompile Include="..\3rdparty\mt19937ar\mt19937ar.c">
<Filter>3rdparty\mt19937ar</Filter>
</ClCompile>
@@ -156,6 +159,9 @@
<ClInclude Include="..\src\char\inter.h">
<Filter>char_sql</Filter>
</ClInclude>
+ <ClInclude Include="..\src\char\pincode.h">
+ <Filter>char_sql</Filter>
+ </ClInclude>
<ClInclude Include="..\src\char\char.h">
<Filter>char_sql</Filter>
</ClInclude>
diff --git a/vcproj-9/char-server.vcproj b/vcproj-9/char-server.vcproj
index b6b284c6d..2a1dfd732 100644
--- a/vcproj-9/char-server.vcproj
+++ b/vcproj-9/char-server.vcproj
@@ -528,6 +528,14 @@
RelativePath="..\src\char\inter.h"
>
</File>
+ <File
+ RelativePath="..\src\char\pincode.c"
+ >
+ </File>
+ <File
+ RelativePath="..\src\char\pincode.h"
+ >
+ </File>
</Filter>
</Files>
<Globals>