account.cpp 35 KB

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