account.cpp 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043
  1. // Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
  2. // For more information, see LICENCE in the main folder
  3. #include "account.hpp"
  4. #include <algorithm> //min / max
  5. #include <cstdlib>
  6. #include <cstring>
  7. #include <common/malloc.hpp>
  8. #include <common/mmo.hpp>
  9. #include <common/showmsg.hpp>
  10. #include <common/socket.hpp>
  11. #include <common/sql.hpp>
  12. #include <common/strlib.hpp>
  13. #include "login.hpp" // login_config
  14. #include "loginchrif.hpp"
  15. /// global defines
  16. /// internal structure
  17. typedef struct AccountDB_SQL {
  18. AccountDB vtable; // public interface
  19. Sql* accounts; // SQL handle accounts storage
  20. std::string db_hostname = "127.0.0.1";
  21. uint16 db_port = 3306;
  22. std::string db_username = "ragnarok";
  23. std::string db_password = "";
  24. std::string db_database = "ragnarok";
  25. std::string codepage = "";
  26. // other settings
  27. bool case_sensitive;
  28. //table name
  29. char account_db[32];
  30. char global_acc_reg_num_table[32];
  31. char global_acc_reg_str_table[32];
  32. } AccountDB_SQL;
  33. /// internal structure
  34. typedef struct AccountDBIterator_SQL {
  35. AccountDBIterator vtable; // public interface
  36. AccountDB_SQL* db;
  37. int32 last_account_id;
  38. } AccountDBIterator_SQL;
  39. /// internal functions
  40. static bool account_db_sql_init(AccountDB* self);
  41. static void account_db_sql_destroy(AccountDB* self);
  42. static bool account_db_sql_get_property(AccountDB* self, const char* key, char* buf, size_t buflen);
  43. static bool account_db_sql_set_property(AccountDB* self, const char* option, const char* value);
  44. static bool account_db_sql_create(AccountDB* self, struct mmo_account* acc);
  45. static bool account_db_sql_remove(AccountDB* self, const uint32 account_id);
  46. static bool account_db_sql_enable_webtoken( AccountDB* self, const uint32 account_id );
  47. static bool account_db_sql_disable_webtoken( AccountDB* self, const uint32 account_id );
  48. static bool account_db_sql_remove_webtokens( AccountDB* self );
  49. static bool account_db_sql_save(AccountDB* self, const struct mmo_account* acc, bool refresh_token);
  50. static bool account_db_sql_load_num(AccountDB* self, struct mmo_account* acc, const uint32 account_id);
  51. static bool account_db_sql_load_str(AccountDB* self, struct mmo_account* acc, const char* userid);
  52. static AccountDBIterator* account_db_sql_iterator(AccountDB* self);
  53. static void account_db_sql_iter_destroy(AccountDBIterator* self);
  54. static bool account_db_sql_iter_next(AccountDBIterator* self, struct mmo_account* acc);
  55. TIMER_FUNC(account_disable_webtoken_timer);
  56. #ifdef VIP_ENABLE
  57. TIMER_FUNC(account_vip_timeout_timer);
  58. bool account_db_sql_enable_monitor_vip( AccountDB* self, const uint32 account_id, time_t vip_time );
  59. bool account_db_sql_disable_monitor_vip( AccountDB* self, const uint32 account_id );
  60. #endif
  61. static bool mmo_auth_fromsql(AccountDB_SQL* db, struct mmo_account* acc, uint32 account_id);
  62. static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, bool is_new, bool refresh_token);
  63. /// public constructor
  64. AccountDB* account_db_sql(void) {
  65. AccountDB_SQL* db = (AccountDB_SQL*)aCalloc(1, sizeof(AccountDB_SQL));
  66. new(db) AccountDB_SQL();
  67. // set up the vtable
  68. db->vtable.init = &account_db_sql_init;
  69. db->vtable.destroy = &account_db_sql_destroy;
  70. db->vtable.get_property = &account_db_sql_get_property;
  71. db->vtable.set_property = &account_db_sql_set_property;
  72. db->vtable.save = &account_db_sql_save;
  73. db->vtable.create = &account_db_sql_create;
  74. db->vtable.remove = &account_db_sql_remove;
  75. db->vtable.enable_webtoken = &account_db_sql_enable_webtoken;
  76. db->vtable.disable_webtoken = &account_db_sql_disable_webtoken;
  77. db->vtable.remove_webtokens = &account_db_sql_remove_webtokens;
  78. db->vtable.load_num = &account_db_sql_load_num;
  79. db->vtable.load_str = &account_db_sql_load_str;
  80. db->vtable.iterator = &account_db_sql_iterator;
  81. #ifdef VIP_ENABLE
  82. db->vtable.enable_monitor_vip = &account_db_sql_enable_monitor_vip;
  83. db->vtable.disable_monitor_vip = &account_db_sql_disable_monitor_vip;
  84. #endif
  85. // initialize to default values
  86. db->accounts = nullptr;
  87. // other settings
  88. db->case_sensitive = false;
  89. safestrncpy(db->account_db, "login", sizeof(db->account_db));
  90. safestrncpy(db->global_acc_reg_num_table, "global_acc_reg_num", sizeof(db->global_acc_reg_num_table));
  91. safestrncpy(db->global_acc_reg_str_table, "global_acc_reg_str", sizeof(db->global_acc_reg_str_table));
  92. add_timer_func_list( account_disable_webtoken_timer, "account_disable_webtoken_timer" );
  93. #ifdef VIP_ENABLE
  94. add_timer_func_list(account_vip_timeout_timer, "account_vip_timeout_timer");
  95. #endif
  96. return &db->vtable;
  97. }
  98. /* ------------------------------------------------------------------------- */
  99. /**
  100. * Establish the database connection.
  101. * @param self: pointer to db
  102. */
  103. static bool account_db_sql_init(AccountDB* self) {
  104. AccountDB_SQL* db = (AccountDB_SQL*)self;
  105. Sql* sql_handle;
  106. db->accounts = Sql_Malloc();
  107. sql_handle = db->accounts;
  108. if( SQL_ERROR == Sql_Connect(sql_handle, db->db_username.c_str(), db->db_password.c_str(), db->db_hostname.c_str(), db->db_port, db->db_database.c_str()) )
  109. {
  110. ShowError("Couldn't connect with uname='%s',host='%s',port='%hu',database='%s'\n",
  111. db->db_username.c_str(), db->db_hostname.c_str(), db->db_port, db->db_database.c_str());
  112. Sql_ShowDebug(sql_handle);
  113. Sql_Free(db->accounts);
  114. db->accounts = nullptr;
  115. return false;
  116. }
  117. if( !db->codepage.empty() && SQL_ERROR == Sql_SetEncoding(sql_handle, db->codepage.c_str()) )
  118. Sql_ShowDebug(sql_handle);
  119. self->remove_webtokens( self );
  120. return true;
  121. }
  122. /**
  123. * Destroy the database and close the connection to it.
  124. * @param self: pointer to db
  125. */
  126. static void account_db_sql_destroy(AccountDB* self){
  127. AccountDB_SQL* db = (AccountDB_SQL*)self;
  128. if( SQL_ERROR == Sql_Query( db->accounts, "UPDATE `%s` SET `web_auth_token` = NULL", db->account_db ) ){
  129. Sql_ShowDebug( db->accounts );
  130. }
  131. Sql_Free(db->accounts);
  132. db->accounts = nullptr;
  133. db->~AccountDB_SQL();
  134. aFree(db);
  135. }
  136. /**
  137. * Get configuration information into buf.
  138. * If the option is supported, adjust the internal state.
  139. * @param self: pointer to db
  140. * @param key: config keyword
  141. * @param buf: value set of the keyword
  142. * @param buflen: size of buffer to avoid out of bound
  143. * @return true if successful, false if something has failed
  144. */
  145. static bool account_db_sql_get_property(AccountDB* self, const char* key, char* buf, size_t buflen)
  146. {
  147. AccountDB_SQL* db = (AccountDB_SQL*)self;
  148. const char* signature;
  149. signature = "login_server_";
  150. if( strncmpi(key, signature, strlen(signature)) == 0 ) {
  151. key += strlen(signature);
  152. if( strcmpi(key, "ip") == 0 )
  153. safesnprintf(buf, buflen, "%s", db->db_hostname.c_str());
  154. else
  155. if( strcmpi(key, "port") == 0 )
  156. safesnprintf(buf, buflen, "%hu", db->db_port);
  157. else
  158. if( strcmpi(key, "id") == 0 )
  159. safesnprintf(buf, buflen, "%s", db->db_username.c_str());
  160. else
  161. if( strcmpi(key, "pw") == 0 )
  162. safesnprintf(buf, buflen, "%s", db->db_password.c_str());
  163. else
  164. if( strcmpi(key, "db") == 0 )
  165. safesnprintf(buf, buflen, "%s", db->db_database.c_str());
  166. else
  167. if( strcmpi(key, "account_db") == 0 )
  168. safesnprintf(buf, buflen, "%s", db->account_db);
  169. else
  170. if( strcmpi(key, "global_acc_reg_str_table") == 0 )
  171. safesnprintf(buf, buflen, "%s", db->global_acc_reg_str_table);
  172. else
  173. if( strcmpi(key, "global_acc_reg_num_table") == 0 )
  174. safesnprintf(buf, buflen, "%s", db->global_acc_reg_num_table);
  175. else
  176. return false;// not found
  177. return true;
  178. }
  179. signature = "login_";
  180. if( strncmpi(key, signature, strlen(signature)) == 0 ) {
  181. key += strlen(signature);
  182. if( strcmpi(key, "codepage") == 0 )
  183. safesnprintf(buf, buflen, "%s", db->codepage.c_str());
  184. else
  185. if( strcmpi(key, "case_sensitive") == 0 )
  186. safesnprintf(buf, buflen, "%d", (db->case_sensitive ? 1 : 0));
  187. else
  188. return false;// not found
  189. return true;
  190. }
  191. return false;// not found
  192. }
  193. /**
  194. * Read and set configuration.
  195. * If the option is supported, adjust the internal state.
  196. * @param self: pointer to db
  197. * @param key: config keyword
  198. * @param value: config value for keyword
  199. * @return true if successful, false if something has failed
  200. */
  201. static bool account_db_sql_set_property(AccountDB* self, const char* key, const char* value) {
  202. AccountDB_SQL* db = (AccountDB_SQL*)self;
  203. const char* signature;
  204. signature = "login_server_";
  205. if( strncmp(key, signature, strlen(signature)) == 0 ) {
  206. key += strlen(signature);
  207. if( strcmpi(key, "ip") == 0 )
  208. db->db_hostname = value;
  209. else
  210. if( strcmpi(key, "port") == 0 )
  211. db->db_port = (uint16)strtoul( value, nullptr, 10 );
  212. else
  213. if( strcmpi(key, "id") == 0 )
  214. db->db_username = value;
  215. else
  216. if( strcmpi(key, "pw") == 0 )
  217. db->db_password = value;
  218. else
  219. if( strcmpi(key, "db") == 0 )
  220. db->db_database = value;
  221. else
  222. if( strcmpi(key, "account_db") == 0 )
  223. safestrncpy(db->account_db, value, sizeof(db->account_db));
  224. else
  225. if( strcmpi(key, "global_acc_reg_str_table") == 0 )
  226. safestrncpy(db->global_acc_reg_str_table, value, sizeof(db->global_acc_reg_str_table));
  227. else
  228. if( strcmpi(key, "global_acc_reg_num_table") == 0 )
  229. safestrncpy(db->global_acc_reg_num_table, value, sizeof(db->global_acc_reg_num_table));
  230. else
  231. return false;// not found
  232. return true;
  233. }
  234. signature = "login_";
  235. if( strncmpi(key, signature, strlen(signature)) == 0 ) {
  236. key += strlen(signature);
  237. if( strcmpi(key, "codepage") == 0 )
  238. db->codepage = value;
  239. else
  240. if( strcmpi(key, "case_sensitive") == 0 )
  241. db->case_sensitive = (config_switch(value)==1);
  242. else
  243. return false;// not found
  244. return true;
  245. }
  246. return false;// not found
  247. }
  248. /**
  249. * Create a new account entry.
  250. * If acc->account_id is -1, the account id will be auto-generated,
  251. * and its value will be written to acc->account_id if everything succeeds.
  252. * @param self: pointer to db
  253. * @param acc: pointer of mmo_account to save
  254. * @return true if successful, false if something has failed
  255. */
  256. static bool account_db_sql_create(AccountDB* self, struct mmo_account* acc) {
  257. AccountDB_SQL* db = (AccountDB_SQL*)self;
  258. Sql* sql_handle = db->accounts;
  259. // decide on the account id to assign
  260. uint32 account_id;
  261. if( acc->account_id != -1 )
  262. {// caller specifies it manually
  263. account_id = acc->account_id;
  264. }
  265. else
  266. {// ask the database
  267. char* data;
  268. size_t len;
  269. if( SQL_SUCCESS != Sql_Query(sql_handle, "SELECT MAX(`account_id`)+1 FROM `%s`", db->account_db) )
  270. {
  271. Sql_ShowDebug(sql_handle);
  272. return false;
  273. }
  274. if( SQL_SUCCESS != Sql_NextRow(sql_handle) )
  275. {
  276. Sql_ShowDebug(sql_handle);
  277. Sql_FreeResult(sql_handle);
  278. return false;
  279. }
  280. Sql_GetData(sql_handle, 0, &data, &len);
  281. account_id = ( data != nullptr ) ? atoi(data) : 0;
  282. Sql_FreeResult(sql_handle);
  283. account_id = max((uint32_t) START_ACCOUNT_NUM, account_id);
  284. }
  285. // zero value is prohibited
  286. if( account_id == 0 )
  287. return false;
  288. // absolute maximum
  289. if( account_id > END_ACCOUNT_NUM )
  290. return false;
  291. // insert the data into the database
  292. acc->account_id = account_id;
  293. return mmo_auth_tosql(db, acc, true, false);
  294. }
  295. /**
  296. * Delete an existing account entry and its regs.
  297. * @param self: pointer to db
  298. * @param account_id: id of user account
  299. * @return true if successful, false if something has failed
  300. */
  301. static bool account_db_sql_remove(AccountDB* self, const uint32 account_id) {
  302. AccountDB_SQL* db = (AccountDB_SQL*)self;
  303. Sql* sql_handle = db->accounts;
  304. bool result = false;
  305. if( SQL_SUCCESS != Sql_QueryStr(sql_handle, "START TRANSACTION")
  306. || SQL_SUCCESS != Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = %d", db->account_db, account_id)
  307. || SQL_SUCCESS != Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = %d", db->global_acc_reg_num_table, account_id)
  308. || SQL_SUCCESS != Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = %d", db->global_acc_reg_str_table, account_id) )
  309. Sql_ShowDebug(sql_handle);
  310. else
  311. result = true;
  312. result &= ( SQL_SUCCESS == Sql_QueryStr(sql_handle, (result == true) ? "COMMIT" : "ROLLBACK") );
  313. return result;
  314. }
  315. /**
  316. * Update an existing account with the new data provided (both account and regs).
  317. * @param self: pointer to db
  318. * @param acc: pointer of mmo_account to save
  319. * @return true if successful, false if something has failed
  320. */
  321. static bool account_db_sql_save(AccountDB* self, const struct mmo_account* acc, bool refresh_token) {
  322. AccountDB_SQL* db = (AccountDB_SQL*)self;
  323. return mmo_auth_tosql(db, acc, false, refresh_token);
  324. }
  325. /**
  326. * Retrieve data from db and store it in the provided data structure.
  327. * Filled data structure is done by delegation to mmo_auth_fromsql.
  328. * @param self: pointer to db
  329. * @param acc: pointer of mmo_account to fill
  330. * @param account_id: id of user account
  331. * @return true if successful, false if something has failed
  332. */
  333. static bool account_db_sql_load_num(AccountDB* self, struct mmo_account* acc, const uint32 account_id) {
  334. AccountDB_SQL* db = (AccountDB_SQL*)self;
  335. return mmo_auth_fromsql(db, acc, account_id);
  336. }
  337. /**
  338. * Retrieve data from db and store it in the provided data structure.
  339. * Doesn't actually retrieve data yet: escapes and checks userid, then transforms it to accid for fetching.
  340. * Filled data structure is done by delegation to account_db_sql_load_num.
  341. * @param self: pointer to db
  342. * @param acc: pointer of mmo_account to fill
  343. * @param userid: name of user account
  344. * @return true if successful, false if something has failed
  345. */
  346. static bool account_db_sql_load_str(AccountDB* self, struct mmo_account* acc, const char* userid) {
  347. AccountDB_SQL* db = (AccountDB_SQL*)self;
  348. Sql* sql_handle = db->accounts;
  349. char esc_userid[2*NAME_LENGTH+1];
  350. uint32 account_id;
  351. char* data;
  352. Sql_EscapeString(sql_handle, esc_userid, userid);
  353. // get the list of account IDs for this user ID
  354. if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `account_id` FROM `%s` WHERE `userid`= %s '%s'",
  355. db->account_db, (db->case_sensitive ? "BINARY" : ""), esc_userid) )
  356. {
  357. Sql_ShowDebug(sql_handle);
  358. return false;
  359. }
  360. if( Sql_NumRows(sql_handle) > 1 )
  361. {// serious problem - duplicit account
  362. ShowError("account_db_sql_load_str: multiple accounts found when retrieving data for account '%s'!\n", userid);
  363. Sql_FreeResult(sql_handle);
  364. return false;
  365. }
  366. if( SQL_SUCCESS != Sql_NextRow(sql_handle) )
  367. {// no such entry
  368. Sql_FreeResult(sql_handle);
  369. return false;
  370. }
  371. Sql_GetData(sql_handle, 0, &data, nullptr);
  372. account_id = atoi(data);
  373. return account_db_sql_load_num(self, acc, account_id);
  374. }
  375. /**
  376. * Create a new forward iterator.
  377. * @param self: pointer to db iterator
  378. * @return a new db iterator
  379. */
  380. static AccountDBIterator* account_db_sql_iterator(AccountDB* self) {
  381. AccountDB_SQL* db = (AccountDB_SQL*)self;
  382. AccountDBIterator_SQL* iter = (AccountDBIterator_SQL*)aCalloc(1, sizeof(AccountDBIterator_SQL));
  383. // set up the vtable
  384. iter->vtable.destroy = &account_db_sql_iter_destroy;
  385. iter->vtable.next = &account_db_sql_iter_next;
  386. // fill data
  387. iter->db = db;
  388. iter->last_account_id = -1;
  389. return &iter->vtable;
  390. }
  391. /**
  392. * Destroys this iterator, releasing all allocated memory (including itself).
  393. * @param self: pointer to db iterator
  394. */
  395. static void account_db_sql_iter_destroy(AccountDBIterator* self) {
  396. AccountDBIterator_SQL* iter = (AccountDBIterator_SQL*)self;
  397. aFree(iter);
  398. }
  399. /**
  400. * Fetches the next account in the database.
  401. * @param self: pointer to db iterator
  402. * @param acc: pointer of mmo_account to fill
  403. * @return true if next account found and filled, false if something has failed
  404. */
  405. static bool account_db_sql_iter_next(AccountDBIterator* self, struct mmo_account* acc) {
  406. AccountDBIterator_SQL* iter = (AccountDBIterator_SQL*)self;
  407. AccountDB_SQL* db = iter->db;
  408. Sql* sql_handle = db->accounts;
  409. char* data;
  410. // get next account ID
  411. if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `account_id` FROM `%s` WHERE `account_id` > '%d' ORDER BY `account_id` ASC LIMIT 1",
  412. db->account_db, iter->last_account_id) )
  413. {
  414. Sql_ShowDebug(sql_handle);
  415. return false;
  416. }
  417. if( SQL_SUCCESS == Sql_NextRow(sql_handle) &&
  418. SQL_SUCCESS == Sql_GetData(sql_handle, 0, &data, nullptr) &&
  419. data != nullptr )
  420. {// get account data
  421. uint32 account_id;
  422. account_id = atoi(data);
  423. if( mmo_auth_fromsql(db, acc, account_id) )
  424. {
  425. iter->last_account_id = account_id;
  426. Sql_FreeResult(sql_handle);
  427. return true;
  428. }
  429. }
  430. Sql_FreeResult(sql_handle);
  431. return false;
  432. }
  433. /**
  434. * Fetch a struct mmo_account from sql, excluding web_auth_token.
  435. * @param db: pointer to db
  436. * @param acc: pointer of mmo_account to fill
  437. * @param account_id: id of user account to take data from
  438. * @return true if successful, false if something has failed
  439. */
  440. static bool mmo_auth_fromsql(AccountDB_SQL* db, struct mmo_account* acc, uint32 account_id) {
  441. Sql* sql_handle = db->accounts;
  442. char* data;
  443. // retrieve login entry for the specified account
  444. if( SQL_ERROR == Sql_Query(sql_handle,
  445. #ifdef VIP_ENABLE
  446. "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`, `vip_time`, `old_group` FROM `%s` WHERE `account_id` = %d",
  447. #else
  448. "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",
  449. #endif
  450. db->account_db, account_id )
  451. ) {
  452. Sql_ShowDebug(sql_handle);
  453. return false;
  454. }
  455. if( SQL_SUCCESS != Sql_NextRow(sql_handle) )
  456. {// no such entry
  457. Sql_FreeResult(sql_handle);
  458. return false;
  459. }
  460. Sql_GetData(sql_handle, 0, &data, nullptr); acc->account_id = atoi(data);
  461. Sql_GetData(sql_handle, 1, &data, nullptr); safestrncpy(acc->userid, data, sizeof(acc->userid));
  462. Sql_GetData(sql_handle, 2, &data, nullptr); safestrncpy(acc->pass, data, sizeof(acc->pass));
  463. Sql_GetData(sql_handle, 3, &data, nullptr); acc->sex = data[0];
  464. Sql_GetData(sql_handle, 4, &data, nullptr); safestrncpy(acc->email, data, sizeof(acc->email));
  465. Sql_GetData(sql_handle, 5, &data, nullptr); acc->group_id = (uint32)atoi(data);
  466. Sql_GetData(sql_handle, 6, &data, nullptr); acc->state = (uint32)strtoul(data, nullptr, 10);
  467. Sql_GetData(sql_handle, 7, &data, nullptr); acc->unban_time = atol(data);
  468. Sql_GetData(sql_handle, 8, &data, nullptr); acc->expiration_time = atol(data);
  469. Sql_GetData(sql_handle, 9, &data, nullptr); acc->logincount = (uint32)strtoul(data, nullptr, 10);
  470. Sql_GetData(sql_handle, 10, &data, nullptr); safestrncpy(acc->lastlogin, data==nullptr?"":data, sizeof(acc->lastlogin));
  471. Sql_GetData(sql_handle, 11, &data, nullptr); safestrncpy(acc->last_ip, data, sizeof(acc->last_ip));
  472. Sql_GetData(sql_handle, 12, &data, nullptr); safestrncpy(acc->birthdate, data==nullptr?"":data, sizeof(acc->birthdate));
  473. Sql_GetData(sql_handle, 13, &data, nullptr); acc->char_slots = (uint8) atoi(data);
  474. Sql_GetData(sql_handle, 14, &data, nullptr); safestrncpy(acc->pincode, data, sizeof(acc->pincode));
  475. Sql_GetData(sql_handle, 15, &data, nullptr); acc->pincode_change = atol(data);
  476. #ifdef VIP_ENABLE
  477. Sql_GetData(sql_handle, 16, &data, nullptr); acc->vip_time = atol(data);
  478. Sql_GetData(sql_handle, 17, &data, nullptr); acc->old_group = atoi(data);
  479. #endif
  480. Sql_FreeResult(sql_handle);
  481. acc->web_auth_token[0] = '\0';
  482. if( acc->char_slots > MAX_CHARS ){
  483. ShowError( "Account %s (AID=%u) exceeds MAX_CHARS. Capping...\n", acc->userid, acc->account_id );
  484. acc->char_slots = MAX_CHARS;
  485. }
  486. return true;
  487. }
  488. /**
  489. * Save a struct mmo_account in sql.
  490. * @param db: pointer to db
  491. * @param acc: pointer of mmo_account to save
  492. * @param is_new: if it's a new entry or should we update
  493. * @return true if successful, false if something has failed
  494. */
  495. static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, bool is_new, bool refresh_token) {
  496. Sql* sql_handle = db->accounts;
  497. SqlStmt stmt{ *sql_handle };
  498. bool result = false;
  499. // try
  500. do
  501. {
  502. if( SQL_SUCCESS != Sql_QueryStr(sql_handle, "START TRANSACTION") )
  503. {
  504. Sql_ShowDebug(sql_handle);
  505. break;
  506. }
  507. if( is_new )
  508. {// insert into account table
  509. if( SQL_SUCCESS != stmt.Prepare(
  510. #ifdef VIP_ENABLE
  511. "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`, `vip_time`, `old_group` ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
  512. #else
  513. "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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
  514. #endif
  515. db->account_db)
  516. || SQL_SUCCESS != stmt.BindParam( 0, SQLDT_INT32, (void*)&acc->account_id, sizeof(acc->account_id))
  517. || SQL_SUCCESS != stmt.BindParam( 1, SQLDT_STRING, (void*)acc->userid, strlen(acc->userid))
  518. || SQL_SUCCESS != stmt.BindParam( 2, SQLDT_STRING, (void*)acc->pass, strlen(acc->pass))
  519. || SQL_SUCCESS != stmt.BindParam( 3, SQLDT_ENUM, (void*)&acc->sex, sizeof(acc->sex))
  520. || SQL_SUCCESS != stmt.BindParam( 4, SQLDT_STRING, (void*)&acc->email, strlen(acc->email))
  521. || SQL_SUCCESS != stmt.BindParam( 5, SQLDT_INT32, (void*)&acc->group_id, sizeof(acc->group_id))
  522. || SQL_SUCCESS != stmt.BindParam( 6, SQLDT_UINT32, (void*)&acc->state, sizeof(acc->state))
  523. || SQL_SUCCESS != stmt.BindParam( 7, SQLDT_LONG, (void*)&acc->unban_time, sizeof(acc->unban_time))
  524. || SQL_SUCCESS != stmt.BindParam( 8, SQLDT_INT32, (void*)&acc->expiration_time, sizeof(acc->expiration_time))
  525. || SQL_SUCCESS != stmt.BindParam( 9, SQLDT_UINT32, (void*)&acc->logincount, sizeof(acc->logincount))
  526. || SQL_SUCCESS != stmt.BindParam(10, acc->lastlogin[0]?SQLDT_STRING:SQLDT_NULL, (void*)&acc->lastlogin, strlen(acc->lastlogin))
  527. || SQL_SUCCESS != stmt.BindParam(11, SQLDT_STRING, (void*)&acc->last_ip, strlen(acc->last_ip))
  528. || SQL_SUCCESS != stmt.BindParam(12, acc->birthdate[0]?SQLDT_STRING:SQLDT_NULL, (void*)&acc->birthdate, strlen(acc->birthdate))
  529. || SQL_SUCCESS != stmt.BindParam(13, SQLDT_UCHAR, (void*)&acc->char_slots, sizeof(acc->char_slots))
  530. || SQL_SUCCESS != stmt.BindParam(14, SQLDT_STRING, (void*)&acc->pincode, strlen(acc->pincode))
  531. || SQL_SUCCESS != stmt.BindParam(15, SQLDT_LONG, (void*)&acc->pincode_change, sizeof(acc->pincode_change))
  532. #ifdef VIP_ENABLE
  533. || SQL_SUCCESS != stmt.BindParam(16, SQLDT_LONG, (void*)&acc->vip_time, sizeof(acc->vip_time))
  534. || SQL_SUCCESS != stmt.BindParam(17, SQLDT_INT32, (void*)&acc->old_group, sizeof(acc->old_group))
  535. #endif
  536. || SQL_SUCCESS != stmt.Execute()
  537. ) {
  538. SqlStmt_ShowDebug(stmt);
  539. break;
  540. }
  541. }
  542. else
  543. {// update account table
  544. if( SQL_SUCCESS != stmt.Prepare(
  545. #ifdef VIP_ENABLE
  546. "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`=?, `vip_time`=?, `old_group`=? WHERE `account_id` = '%d'",
  547. #else
  548. "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'",
  549. #endif
  550. db->account_db, acc->account_id)
  551. || SQL_SUCCESS != stmt.BindParam( 0, SQLDT_STRING, (void*)acc->userid, strlen(acc->userid))
  552. || SQL_SUCCESS != stmt.BindParam( 1, SQLDT_STRING, (void*)acc->pass, strlen(acc->pass))
  553. || SQL_SUCCESS != stmt.BindParam( 2, SQLDT_ENUM, (void*)&acc->sex, sizeof(acc->sex))
  554. || SQL_SUCCESS != stmt.BindParam( 3, SQLDT_STRING, (void*)acc->email, strlen(acc->email))
  555. || SQL_SUCCESS != stmt.BindParam( 4, SQLDT_INT32, (void*)&acc->group_id, sizeof(acc->group_id))
  556. || SQL_SUCCESS != stmt.BindParam( 5, SQLDT_UINT32, (void*)&acc->state, sizeof(acc->state))
  557. || SQL_SUCCESS != stmt.BindParam( 6, SQLDT_LONG, (void*)&acc->unban_time, sizeof(acc->unban_time))
  558. || SQL_SUCCESS != stmt.BindParam( 7, SQLDT_LONG, (void*)&acc->expiration_time, sizeof(acc->expiration_time))
  559. || SQL_SUCCESS != stmt.BindParam( 8, SQLDT_UINT32, (void*)&acc->logincount, sizeof(acc->logincount))
  560. || SQL_SUCCESS != stmt.BindParam( 9, acc->lastlogin[0]?SQLDT_STRING:SQLDT_NULL, (void*)&acc->lastlogin, strlen(acc->lastlogin))
  561. || SQL_SUCCESS != stmt.BindParam(10, SQLDT_STRING, (void*)&acc->last_ip, strlen(acc->last_ip))
  562. || SQL_SUCCESS != stmt.BindParam(11, acc->birthdate[0]?SQLDT_STRING:SQLDT_NULL, (void*)&acc->birthdate, strlen(acc->birthdate))
  563. || SQL_SUCCESS != stmt.BindParam(12, SQLDT_UCHAR, (void*)&acc->char_slots, sizeof(acc->char_slots))
  564. || SQL_SUCCESS != stmt.BindParam(13, SQLDT_STRING, (void*)&acc->pincode, strlen(acc->pincode))
  565. || SQL_SUCCESS != stmt.BindParam(14, SQLDT_LONG, (void*)&acc->pincode_change, sizeof(acc->pincode_change))
  566. #ifdef VIP_ENABLE
  567. || SQL_SUCCESS != stmt.BindParam(15, SQLDT_LONG, (void*)&acc->vip_time, sizeof(acc->vip_time))
  568. || SQL_SUCCESS != stmt.BindParam(16, SQLDT_INT32, (void*)&acc->old_group, sizeof(acc->old_group))
  569. #endif
  570. || SQL_SUCCESS != stmt.Execute()
  571. ) {
  572. SqlStmt_ShowDebug(stmt);
  573. break;
  574. }
  575. }
  576. if( acc->sex != 'S' && login_config.use_web_auth_token && refresh_token ){
  577. static bool initialized = false;
  578. static const char* query;
  579. // Pseudo Scope to break out
  580. while( !initialized ){
  581. if( SQL_SUCCESS == Sql_Query( sql_handle, "SELECT SHA2( 'test', 256 )" ) ){
  582. query = "UPDATE `%s` SET `web_auth_token` = LEFT( SHA2( CONCAT( UUID(), RAND() ), 256 ), %d ), `web_auth_token_enabled` = '1' WHERE `account_id` = '%d'";
  583. initialized = true;
  584. break;
  585. }
  586. if( SQL_SUCCESS == Sql_Query( sql_handle, "SELECT MD5( 'test' )" ) ){
  587. query = "UPDATE `%s` SET `web_auth_token` = LEFT( MD5( CONCAT( UUID(), RAND() ) ), %d ), `web_auth_token_enabled` = '1' WHERE `account_id` = '%d'";
  588. initialized = true;
  589. break;
  590. }
  591. ShowWarning( "Your MySQL does not support SHA2 and MD5 - no hashing will be used for login token creation.\n" );
  592. ShowWarning( "If you are using an old version of MySQL consider upgrading to a newer release.\n" );
  593. query = "UPDATE `%s` SET `web_auth_token` = LEFT( CONCAT( UUID(), RAND() ), %d ), `web_auth_token_enabled` = '1' WHERE `account_id` = '%d'";
  594. initialized = true;
  595. break;
  596. }
  597. const int32 MAX_RETRIES = 20;
  598. int32 i = 0;
  599. bool success = false;
  600. // Retry it for a maximum number of retries
  601. do{
  602. if( SQL_SUCCESS == Sql_Query( sql_handle, query, db->account_db, WEB_AUTH_TOKEN_LENGTH - 1, acc->account_id ) ){
  603. success = true;
  604. break;
  605. }
  606. }while( i < MAX_RETRIES && Sql_GetError( sql_handle ) == 1062 );
  607. if( !success ){
  608. if( i == MAX_RETRIES ){
  609. ShowError( "Failed to generate a unique web_auth_token with %d retries...\n", i );
  610. }else{
  611. Sql_ShowDebug( sql_handle );
  612. }
  613. break;
  614. }
  615. char* data;
  616. size_t len;
  617. if( SQL_SUCCESS != Sql_Query( sql_handle, "SELECT `web_auth_token` from `%s` WHERE `account_id` = '%d'", db->account_db, acc->account_id ) ||
  618. SQL_SUCCESS != Sql_NextRow( sql_handle ) ||
  619. SQL_SUCCESS != Sql_GetData( sql_handle, 0, &data, &len )
  620. ){
  621. Sql_ShowDebug( sql_handle );
  622. break;
  623. }
  624. safestrncpy( (char *)&acc->web_auth_token, data, sizeof( acc->web_auth_token ) );
  625. Sql_FreeResult( sql_handle );
  626. }
  627. // if we got this far, everything was successful
  628. result = true;
  629. } while(0);
  630. // finally
  631. result &= ( SQL_SUCCESS == Sql_QueryStr(sql_handle, (result == true) ? "COMMIT" : "ROLLBACK") );
  632. return result;
  633. }
  634. void mmo_save_global_accreg(AccountDB* self, int32 fd, uint32 account_id, uint32 char_id) {
  635. Sql* sql_handle = ((AccountDB_SQL*)self)->accounts;
  636. AccountDB_SQL* db = (AccountDB_SQL*)self;
  637. uint16 count = RFIFOW(fd, 12);
  638. if (count) {
  639. int32 cursor = 14, i;
  640. char key[32], sval[254], esc_key[32*2+1], esc_sval[254*2+1];
  641. for (i = 0; i < count; i++) {
  642. uint32 index;
  643. safestrncpy(key, RFIFOCP(fd, cursor + 1), RFIFOB(fd, cursor));
  644. Sql_EscapeString(sql_handle, esc_key, key);
  645. cursor += RFIFOB(fd, cursor) + 1;
  646. index = RFIFOL(fd, cursor);
  647. cursor += 4;
  648. switch (RFIFOB(fd, cursor++)) {
  649. // int32
  650. case 0:
  651. if( SQL_ERROR == Sql_Query(sql_handle, "REPLACE INTO `%s` (`account_id`,`key`,`index`,`value`) VALUES ('%" PRIu32 "','%s','%" PRIu32 "','%" PRId64 "')", db->global_acc_reg_num_table, account_id, esc_key, index, RFIFOQ(fd, cursor)) )
  652. Sql_ShowDebug(sql_handle);
  653. cursor += 8;
  654. break;
  655. case 1:
  656. if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = '%" PRIu32 "' AND `key` = '%s' AND `index` = '%" PRIu32 "' LIMIT 1", db->global_acc_reg_num_table, account_id, esc_key, index) )
  657. Sql_ShowDebug(sql_handle);
  658. break;
  659. // str
  660. case 2:
  661. safestrncpy(sval, RFIFOCP(fd, cursor + 1), RFIFOB(fd, cursor));
  662. cursor += RFIFOB(fd, cursor) + 1;
  663. Sql_EscapeString(sql_handle, esc_sval, sval);
  664. if( SQL_ERROR == Sql_Query(sql_handle, "REPLACE INTO `%s` (`account_id`,`key`,`index`,`value`) VALUES ('%" PRIu32 "','%s','%" PRIu32 "','%s')", db->global_acc_reg_str_table, account_id, esc_key, index, esc_sval) )
  665. Sql_ShowDebug(sql_handle);
  666. break;
  667. case 3:
  668. if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = '%" PRIu32 "' AND `key` = '%s' AND `index` = '%" PRIu32 "' LIMIT 1", db->global_acc_reg_str_table, account_id, esc_key, index) )
  669. Sql_ShowDebug(sql_handle);
  670. break;
  671. default:
  672. ShowError("mmo_save_global_accreg: unknown type %d\n",RFIFOB(fd, cursor - 1));
  673. return;
  674. }
  675. }
  676. }
  677. }
  678. void mmo_send_global_accreg(AccountDB* self, int32 fd, uint32 account_id, uint32 char_id) {
  679. Sql* sql_handle = ((AccountDB_SQL*)self)->accounts;
  680. AccountDB_SQL* db = (AccountDB_SQL*)self;
  681. char* data;
  682. int16 plen = 0;
  683. size_t len;
  684. if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `key`, `index`, `value` FROM `%s` WHERE `account_id`='%" PRIu32 "'", db->global_acc_reg_str_table, account_id) )
  685. Sql_ShowDebug(sql_handle);
  686. WFIFOHEAD(fd, 60000 + 300);
  687. WFIFOW(fd, 0) = 0x2726;
  688. // 0x2 = length, set prior to being sent
  689. WFIFOL(fd, 4) = account_id;
  690. WFIFOL(fd, 8) = char_id;
  691. WFIFOB(fd, 12) = 0; // var type (only set when all vars have been sent, regardless of type)
  692. WFIFOB(fd, 13) = 1; // is string type
  693. WFIFOW(fd, 14) = 0; // count
  694. plen = 16;
  695. /**
  696. * Vessel!
  697. *
  698. * str type
  699. * { keyLength(B), key(<keyLength>), index(L), valLength(B), val(<valLength>) }
  700. **/
  701. while ( SQL_SUCCESS == Sql_NextRow(sql_handle) ) {
  702. Sql_GetData(sql_handle, 0, &data, nullptr);
  703. len = strlen(data)+1;
  704. WFIFOB(fd, plen) = (unsigned char)len; // won't be higher; the column size is 32
  705. plen += 1;
  706. safestrncpy(WFIFOCP(fd,plen), data, len);
  707. plen += static_cast<decltype(plen)>( len );
  708. Sql_GetData(sql_handle, 1, &data, nullptr);
  709. WFIFOL(fd, plen) = (uint32)atol(data);
  710. plen += 4;
  711. Sql_GetData(sql_handle, 2, &data, nullptr);
  712. len = strlen(data)+1;
  713. WFIFOB(fd, plen) = (unsigned char)len; // won't be higher; the column size is 254
  714. plen += 1;
  715. safestrncpy(WFIFOCP(fd,plen), data, len);
  716. plen += static_cast<decltype(plen)>( len );
  717. WFIFOW(fd, 14) += 1;
  718. if( plen > 60000 ) {
  719. WFIFOW(fd, 2) = plen;
  720. WFIFOSET(fd, plen);
  721. // prepare follow up
  722. WFIFOHEAD(fd, 60000 + 300);
  723. WFIFOW(fd, 0) = 0x2726;
  724. // 0x2 = length, set prior to being sent
  725. WFIFOL(fd, 4) = account_id;
  726. WFIFOL(fd, 8) = char_id;
  727. WFIFOB(fd, 12) = 0; // var type (only set when all vars have been sent, regardless of type)
  728. WFIFOB(fd, 13) = 1; // is string type
  729. WFIFOW(fd, 14) = 0; // count
  730. plen = 16;
  731. }
  732. }
  733. WFIFOW(fd, 2) = plen;
  734. WFIFOSET(fd, plen);
  735. Sql_FreeResult(sql_handle);
  736. if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `key`, `index`, `value` FROM `%s` WHERE `account_id`='%" PRIu32 "'", db->global_acc_reg_num_table, account_id) )
  737. Sql_ShowDebug(sql_handle);
  738. WFIFOHEAD(fd, 60000 + 300);
  739. WFIFOW(fd, 0) = 0x2726;
  740. // 0x2 = length, set prior to being sent
  741. WFIFOL(fd, 4) = account_id;
  742. WFIFOL(fd, 8) = char_id;
  743. WFIFOB(fd, 12) = 0; // var type (only set when all vars have been sent, regardless of type)
  744. WFIFOB(fd, 13) = 0; // is int32 type
  745. WFIFOW(fd, 14) = 0; // count
  746. plen = 16;
  747. /**
  748. * Vessel!
  749. *
  750. * int32 type
  751. * { keyLength(B), key(<keyLength>), index(L), value(L) }
  752. **/
  753. while ( SQL_SUCCESS == Sql_NextRow(sql_handle) ) {
  754. Sql_GetData(sql_handle, 0, &data, nullptr);
  755. len = strlen(data)+1;
  756. WFIFOB(fd, plen) = (unsigned char)len; // won't be higher; the column size is 32
  757. plen += 1;
  758. safestrncpy(WFIFOCP(fd,plen), data, len);
  759. plen += static_cast<decltype(plen)>( len );
  760. Sql_GetData(sql_handle, 1, &data, nullptr);
  761. WFIFOL(fd, plen) = (uint32)atol(data);
  762. plen += 4;
  763. Sql_GetData(sql_handle, 2, &data, nullptr);
  764. WFIFOQ(fd, plen) = strtoll(data,nullptr,10);
  765. plen += 8;
  766. WFIFOW(fd, 14) += 1;
  767. if( plen > 60000 ) {
  768. WFIFOW(fd, 2) = plen;
  769. WFIFOSET(fd, plen);
  770. // prepare follow up
  771. WFIFOHEAD(fd, 60000 + 300);
  772. WFIFOW(fd, 0) = 0x2726;
  773. // 0x2 = length, set prior to being sent
  774. WFIFOL(fd, 4) = account_id;
  775. WFIFOL(fd, 8) = char_id;
  776. WFIFOB(fd, 12) = 0; // var type (only set when all vars have been sent, regardless of type)
  777. WFIFOB(fd, 13) = 0; // is int32 type
  778. WFIFOW(fd, 14) = 0; // count
  779. plen = 16;
  780. }
  781. }
  782. WFIFOB(fd, 12) = 1;
  783. WFIFOW(fd, 2) = plen;
  784. WFIFOSET(fd, plen);
  785. Sql_FreeResult(sql_handle);
  786. }
  787. bool account_db_sql_enable_webtoken( AccountDB* self, const uint32 account_id ){
  788. AccountDB_SQL* db = (AccountDB_SQL*)self;
  789. if( SQL_ERROR == Sql_Query( db->accounts, "UPDATE `%s` SET `web_auth_token_enabled` = '1' WHERE `account_id` = '%u'", db->account_db, account_id ) ){
  790. Sql_ShowDebug( db->accounts );
  791. return false;
  792. }
  793. return true;
  794. }
  795. /**
  796. * Timered function to disable webtoken for user
  797. * If the user is online, then they must have logged since we started the timer.
  798. * In that case, do nothing. The new authtoken must be valid.
  799. * @param tid: timer id
  800. * @param tick: tick of execution
  801. * @param id: user account id
  802. * @param data: AccountDB // because we don't use singleton???
  803. * @return :0
  804. */
  805. TIMER_FUNC(account_disable_webtoken_timer){
  806. const struct online_login_data* p = login_get_online_user(id);
  807. AccountDB_SQL* db = reinterpret_cast<AccountDB_SQL*>(data);
  808. if (p == nullptr) {
  809. ShowInfo("Web Auth Token for account %d was disabled\n", id);
  810. if( SQL_ERROR == Sql_Query( db->accounts, "UPDATE `%s` SET `web_auth_token_enabled` = '0' WHERE `account_id` = '%u'", db->account_db, id ) ){
  811. Sql_ShowDebug( db->accounts );
  812. return 0;
  813. }
  814. }
  815. return 0;
  816. }
  817. bool account_db_sql_disable_webtoken( AccountDB* self, const uint32 account_id ){
  818. AccountDB_SQL* db = (AccountDB_SQL*)self;
  819. add_timer(gettick() + login_config.disable_webtoken_delay, account_disable_webtoken_timer, account_id, reinterpret_cast<intptr_t>(db));
  820. return true;
  821. }
  822. bool account_db_sql_remove_webtokens( AccountDB* self ){
  823. AccountDB_SQL* db = (AccountDB_SQL*)self;
  824. if( SQL_ERROR == Sql_Query( db->accounts, "UPDATE `%s` SET `web_auth_token` = NULL, `web_auth_token_enabled` = '0'", db->account_db ) ){
  825. Sql_ShowDebug( db->accounts );
  826. return false;
  827. }
  828. return true;
  829. }
  830. #ifdef VIP_ENABLE
  831. TIMER_FUNC(account_vip_timeout_timer){
  832. struct online_login_data* ld = login_get_online_user( id );
  833. AccountDB* db = reinterpret_cast<AccountDB*>( data );
  834. // Player is not online anymore
  835. if( ld == nullptr ){
  836. return 0;
  837. }
  838. ld->vip_timeout_tid = INVALID_TIMER;
  839. struct mmo_account acc;
  840. if( db->load_num( db, &acc, id ) ){
  841. time_t now = time( nullptr );
  842. time_t vip_time = acc.vip_time;
  843. bool isvip;
  844. // Is still VIP
  845. if( now < vip_time ){
  846. t_tick remaining = vip_time - now;
  847. ld->vip_timeout_tid = add_timer( gettick() + remaining * 1000, account_vip_timeout_timer, id, data );
  848. isvip = true;
  849. }else{
  850. isvip = false;
  851. }
  852. logchrif_sendvipdata( ch_server[ld->char_server].fd, &acc, isvip ? 0x1 : 0x0, -1 );
  853. }
  854. return 0;
  855. }
  856. bool account_db_sql_enable_monitor_vip( AccountDB* self, const uint32 account_id, time_t vip_time ){
  857. struct online_login_data* ld = login_get_online_user( account_id );
  858. if( ld == nullptr ){
  859. return false;
  860. }
  861. if( ld->vip_timeout_tid != INVALID_TIMER ){
  862. delete_timer( ld->vip_timeout_tid, account_vip_timeout_timer );
  863. ld->vip_timeout_tid = INVALID_TIMER;
  864. }
  865. time_t now = time(nullptr);
  866. t_tick remaining = vip_time - now;
  867. ld->vip_timeout_tid = add_timer( gettick() + remaining * 1000, account_vip_timeout_timer, account_id, reinterpret_cast<intptr_t>( self ) );
  868. return true;
  869. }
  870. bool account_db_sql_disable_monitor_vip( AccountDB* self, const uint32 account_id ){
  871. AccountDB_SQL* db = (AccountDB_SQL*)self;
  872. struct online_login_data* ld = login_get_online_user( account_id );
  873. if( ld == nullptr ){
  874. return false;
  875. }
  876. if( ld->vip_timeout_tid != INVALID_TIMER ){
  877. delete_timer( ld->vip_timeout_tid, account_vip_timeout_timer );
  878. ld->vip_timeout_tid = INVALID_TIMER;
  879. }
  880. return true;
  881. }
  882. #endif