account_sql.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689
  1. // Copyright (c) Athena Dev Teams - Licensed under GNU GPL
  2. // For more information, see LICENCE in the main folder
  3. #include "../common/malloc.h"
  4. #include "../common/mmo.h"
  5. #include "../common/showmsg.h"
  6. #include "../common/sql.h"
  7. #include "../common/strlib.h"
  8. #include "../common/timer.h"
  9. #include "account.h"
  10. #include <stdlib.h>
  11. #include <string.h>
  12. /// global defines
  13. #define ACCOUNT_SQL_DB_VERSION 20110114
  14. /// internal structure
  15. typedef struct AccountDB_SQL
  16. {
  17. AccountDB vtable; // public interface
  18. Sql* accounts; // SQL accounts storage
  19. // global sql settings
  20. char global_db_hostname[32];
  21. uint16 global_db_port;
  22. char global_db_username[32];
  23. char global_db_password[32];
  24. char global_db_database[32];
  25. char global_codepage[32];
  26. // local sql settings
  27. char db_hostname[32];
  28. uint16 db_port;
  29. char db_username[32];
  30. char db_password[32];
  31. char db_database[32];
  32. char codepage[32];
  33. // other settings
  34. bool case_sensitive;
  35. char account_db[32];
  36. char accreg_db[32];
  37. } AccountDB_SQL;
  38. /// internal structure
  39. typedef struct AccountDBIterator_SQL
  40. {
  41. AccountDBIterator vtable; // public interface
  42. AccountDB_SQL* db;
  43. int last_account_id;
  44. } AccountDBIterator_SQL;
  45. /// internal functions
  46. static bool account_db_sql_init(AccountDB* self);
  47. static void account_db_sql_destroy(AccountDB* self);
  48. static bool account_db_sql_get_property(AccountDB* self, const char* key, char* buf, size_t buflen);
  49. static bool account_db_sql_set_property(AccountDB* self, const char* option, const char* value);
  50. static bool account_db_sql_create(AccountDB* self, struct mmo_account* acc);
  51. static bool account_db_sql_remove(AccountDB* self, const int account_id);
  52. static bool account_db_sql_save(AccountDB* self, const struct mmo_account* acc);
  53. static bool account_db_sql_load_num(AccountDB* self, struct mmo_account* acc, const int account_id);
  54. static bool account_db_sql_load_str(AccountDB* self, struct mmo_account* acc, const char* userid);
  55. static AccountDBIterator* account_db_sql_iterator(AccountDB* self);
  56. static void account_db_sql_iter_destroy(AccountDBIterator* self);
  57. static bool account_db_sql_iter_next(AccountDBIterator* self, struct mmo_account* acc);
  58. static bool mmo_auth_fromsql(AccountDB_SQL* db, struct mmo_account* acc, int account_id);
  59. static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, bool is_new);
  60. /// public constructor
  61. AccountDB* account_db_sql(void)
  62. {
  63. AccountDB_SQL* db = (AccountDB_SQL*)aCalloc(1, sizeof(AccountDB_SQL));
  64. // set up the vtable
  65. db->vtable.init = &account_db_sql_init;
  66. db->vtable.destroy = &account_db_sql_destroy;
  67. db->vtable.get_property = &account_db_sql_get_property;
  68. db->vtable.set_property = &account_db_sql_set_property;
  69. db->vtable.save = &account_db_sql_save;
  70. db->vtable.create = &account_db_sql_create;
  71. db->vtable.remove = &account_db_sql_remove;
  72. db->vtable.load_num = &account_db_sql_load_num;
  73. db->vtable.load_str = &account_db_sql_load_str;
  74. db->vtable.iterator = &account_db_sql_iterator;
  75. // initialize to default values
  76. db->accounts = NULL;
  77. // global sql settings
  78. safestrncpy(db->global_db_hostname, "127.0.0.1", sizeof(db->global_db_hostname));
  79. db->global_db_port = 3306;
  80. safestrncpy(db->global_db_username, "ragnarok", sizeof(db->global_db_username));
  81. safestrncpy(db->global_db_password, "ragnarok", sizeof(db->global_db_password));
  82. safestrncpy(db->global_db_database, "ragnarok", sizeof(db->global_db_database));
  83. safestrncpy(db->global_codepage, "", sizeof(db->global_codepage));
  84. // local sql settings
  85. safestrncpy(db->db_hostname, "", sizeof(db->db_hostname));
  86. db->db_port = 3306;
  87. safestrncpy(db->db_username, "", sizeof(db->db_username));
  88. safestrncpy(db->db_password, "", sizeof(db->db_password));
  89. safestrncpy(db->db_database, "", sizeof(db->db_database));
  90. safestrncpy(db->codepage, "", sizeof(db->codepage));
  91. // other settings
  92. db->case_sensitive = false;
  93. safestrncpy(db->account_db, "login", sizeof(db->account_db));
  94. safestrncpy(db->accreg_db, "global_reg_value", sizeof(db->accreg_db));
  95. return &db->vtable;
  96. }
  97. /* ------------------------------------------------------------------------- */
  98. /// establishes database connection
  99. static bool account_db_sql_init(AccountDB* self)
  100. {
  101. AccountDB_SQL* db = (AccountDB_SQL*)self;
  102. Sql* sql_handle;
  103. const char* username;
  104. const char* password;
  105. const char* hostname;
  106. uint16 port;
  107. const char* database;
  108. const char* codepage;
  109. db->accounts = Sql_Malloc();
  110. sql_handle = db->accounts;
  111. if( db->db_hostname[0] != '\0' )
  112. {// local settings
  113. username = db->db_username;
  114. password = db->db_password;
  115. hostname = db->db_hostname;
  116. port = db->db_port;
  117. database = db->db_database;
  118. codepage = db->codepage;
  119. }
  120. else
  121. {// global settings
  122. username = db->global_db_username;
  123. password = db->global_db_password;
  124. hostname = db->global_db_hostname;
  125. port = db->global_db_port;
  126. database = db->global_db_database;
  127. codepage = db->global_codepage;
  128. }
  129. if( SQL_ERROR == Sql_Connect(sql_handle, username, password, hostname, port, database) )
  130. {
  131. Sql_ShowDebug(sql_handle);
  132. Sql_Free(db->accounts);
  133. db->accounts = NULL;
  134. return false;
  135. }
  136. if( codepage[0] != '\0' && SQL_ERROR == Sql_SetEncoding(sql_handle, codepage) )
  137. Sql_ShowDebug(sql_handle);
  138. return true;
  139. }
  140. /// disconnects from database
  141. static void account_db_sql_destroy(AccountDB* self)
  142. {
  143. AccountDB_SQL* db = (AccountDB_SQL*)self;
  144. Sql_Free(db->accounts);
  145. db->accounts = NULL;
  146. aFree(db);
  147. }
  148. /// Gets a property from this database.
  149. static bool account_db_sql_get_property(AccountDB* self, const char* key, char* buf, size_t buflen)
  150. {
  151. AccountDB_SQL* db = (AccountDB_SQL*)self;
  152. const char* signature;
  153. signature = "engine.";
  154. if( strncmpi(key, signature, strlen(signature)) == 0 )
  155. {
  156. key += strlen(signature);
  157. if( strcmpi(key, "name") == 0 )
  158. safesnprintf(buf, buflen, "sql");
  159. else
  160. if( strcmpi(key, "version") == 0 )
  161. safesnprintf(buf, buflen, "%d", ACCOUNT_SQL_DB_VERSION);
  162. else
  163. if( strcmpi(key, "comment") == 0 )
  164. safesnprintf(buf, buflen, "SQL Account Database");
  165. else
  166. return false;// not found
  167. return true;
  168. }
  169. signature = "sql.";
  170. if( strncmpi(key, signature, strlen(signature)) == 0 )
  171. {
  172. key += strlen(signature);
  173. if( strcmpi(key, "db_hostname") == 0 )
  174. safesnprintf(buf, buflen, "%s", db->global_db_hostname);
  175. else
  176. if( strcmpi(key, "db_port") == 0 )
  177. safesnprintf(buf, buflen, "%d", db->global_db_port);
  178. else
  179. if( strcmpi(key, "db_username") == 0 )
  180. safesnprintf(buf, buflen, "%s", db->global_db_username);
  181. else
  182. if( strcmpi(key, "db_password") == 0 )
  183. safesnprintf(buf, buflen, "%s", db->global_db_password);
  184. else
  185. if( strcmpi(key, "db_database") == 0 )
  186. safesnprintf(buf, buflen, "%s", db->global_db_database);
  187. else
  188. if( strcmpi(key, "codepage") == 0 )
  189. safesnprintf(buf, buflen, "%s", db->global_codepage);
  190. else
  191. return false;// not found
  192. return true;
  193. }
  194. signature = "account.sql.";
  195. if( strncmpi(key, signature, strlen(signature)) == 0 )
  196. {
  197. key += strlen(signature);
  198. if( strcmpi(key, "db_hostname") == 0 )
  199. safesnprintf(buf, buflen, "%s", db->db_hostname);
  200. else
  201. if( strcmpi(key, "db_port") == 0 )
  202. safesnprintf(buf, buflen, "%d", db->db_port);
  203. else
  204. if( strcmpi(key, "db_username") == 0 )
  205. safesnprintf(buf, buflen, "%s", db->db_username);
  206. else
  207. if( strcmpi(key, "db_password") == 0 )
  208. safesnprintf(buf, buflen, "%s", db->db_password);
  209. else
  210. if( strcmpi(key, "db_database") == 0 )
  211. safesnprintf(buf, buflen, "%s", db->db_database);
  212. else
  213. if( strcmpi(key, "codepage") == 0 )
  214. safesnprintf(buf, buflen, "%s", db->codepage);
  215. else
  216. if( strcmpi(key, "case_sensitive") == 0 )
  217. safesnprintf(buf, buflen, "%d", (db->case_sensitive ? 1 : 0));
  218. else
  219. if( strcmpi(key, "account_db") == 0 )
  220. safesnprintf(buf, buflen, "%s", db->account_db);
  221. else
  222. if( strcmpi(key, "accreg_db") == 0 )
  223. safesnprintf(buf, buflen, "%s", db->accreg_db);
  224. else
  225. return false;// not found
  226. return true;
  227. }
  228. return false;// not found
  229. }
  230. /// if the option is supported, adjusts the internal state
  231. static bool account_db_sql_set_property(AccountDB* self, const char* key, const char* value)
  232. {
  233. AccountDB_SQL* db = (AccountDB_SQL*)self;
  234. const char* signature;
  235. signature = "sql.";
  236. if( strncmp(key, signature, strlen(signature)) == 0 )
  237. {
  238. key += strlen(signature);
  239. if( strcmpi(key, "db_hostname") == 0 )
  240. safestrncpy(db->global_db_hostname, value, sizeof(db->global_db_hostname));
  241. else
  242. if( strcmpi(key, "db_port") == 0 )
  243. db->global_db_port = (uint16)strtoul(value, NULL, 10);
  244. else
  245. if( strcmpi(key, "db_username") == 0 )
  246. safestrncpy(db->global_db_username, value, sizeof(db->global_db_username));
  247. else
  248. if( strcmpi(key, "db_password") == 0 )
  249. safestrncpy(db->global_db_password, value, sizeof(db->global_db_password));
  250. else
  251. if( strcmpi(key, "db_database") == 0 )
  252. safestrncpy(db->global_db_database, value, sizeof(db->global_db_database));
  253. else
  254. if( strcmpi(key, "codepage") == 0 )
  255. safestrncpy(db->global_codepage, value, sizeof(db->global_codepage));
  256. else
  257. return false;// not found
  258. return true;
  259. }
  260. signature = "account.sql.";
  261. if( strncmp(key, signature, strlen(signature)) == 0 )
  262. {
  263. key += strlen(signature);
  264. if( strcmpi(key, "db_hostname") == 0 )
  265. safestrncpy(db->db_hostname, value, sizeof(db->db_hostname));
  266. else
  267. if( strcmpi(key, "db_port") == 0 )
  268. db->db_port = (uint16)strtoul(value, NULL, 10);
  269. else
  270. if( strcmpi(key, "db_username") == 0 )
  271. safestrncpy(db->db_username, value, sizeof(db->db_username));
  272. else
  273. if( strcmpi(key, "db_password") == 0 )
  274. safestrncpy(db->db_password, value, sizeof(db->db_password));
  275. else
  276. if( strcmpi(key, "db_database") == 0 )
  277. safestrncpy(db->db_database, value, sizeof(db->db_database));
  278. else
  279. if( strcmpi(key, "codepage") == 0 )
  280. safestrncpy(db->codepage, value, sizeof(db->codepage));
  281. else
  282. if( strcmpi(key, "case_sensitive") == 0 )
  283. db->case_sensitive = config_switch(value);
  284. else
  285. if( strcmpi(key, "account_db") == 0 )
  286. safestrncpy(db->account_db, value, sizeof(db->account_db));
  287. else
  288. if( strcmpi(key, "accreg_db") == 0 )
  289. safestrncpy(db->accreg_db, value, sizeof(db->accreg_db));
  290. else
  291. return false;// not found
  292. return true;
  293. }
  294. return false;// not found
  295. }
  296. /// create a new account entry
  297. /// If acc->account_id is -1, the account id will be auto-generated,
  298. /// and its value will be written to acc->account_id if everything succeeds.
  299. static bool account_db_sql_create(AccountDB* self, struct mmo_account* acc)
  300. {
  301. AccountDB_SQL* db = (AccountDB_SQL*)self;
  302. Sql* sql_handle = db->accounts;
  303. // decide on the account id to assign
  304. int account_id;
  305. if( acc->account_id != -1 )
  306. {// caller specifies it manually
  307. account_id = acc->account_id;
  308. }
  309. else
  310. {// ask the database
  311. char* data;
  312. size_t len;
  313. if( SQL_SUCCESS != Sql_Query(sql_handle, "SELECT MAX(`account_id`)+1 FROM `%s`", db->account_db) )
  314. {
  315. Sql_ShowDebug(sql_handle);
  316. return false;
  317. }
  318. if( SQL_SUCCESS != Sql_NextRow(sql_handle) )
  319. {
  320. Sql_ShowDebug(sql_handle);
  321. Sql_FreeResult(sql_handle);
  322. return false;
  323. }
  324. Sql_GetData(sql_handle, 0, &data, &len);
  325. account_id = ( data != NULL ) ? atoi(data) : 0;
  326. Sql_FreeResult(sql_handle);
  327. if( account_id < START_ACCOUNT_NUM )
  328. account_id = START_ACCOUNT_NUM;
  329. }
  330. // zero value is prohibited
  331. if( account_id == 0 )
  332. return false;
  333. // absolute maximum
  334. if( account_id > END_ACCOUNT_NUM )
  335. return false;
  336. // insert the data into the database
  337. acc->account_id = account_id;
  338. return mmo_auth_tosql(db, acc, true);
  339. }
  340. /// delete an existing account entry + its regs
  341. static bool account_db_sql_remove(AccountDB* self, const int account_id)
  342. {
  343. AccountDB_SQL* db = (AccountDB_SQL*)self;
  344. Sql* sql_handle = db->accounts;
  345. bool result = false;
  346. if( SQL_SUCCESS != Sql_QueryStr(sql_handle, "START TRANSACTION")
  347. || SQL_SUCCESS != Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = %d", db->account_db, account_id)
  348. || SQL_SUCCESS != Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = %d", db->accreg_db, account_id) )
  349. Sql_ShowDebug(sql_handle);
  350. else
  351. result = true;
  352. result &= ( SQL_SUCCESS == Sql_QueryStr(sql_handle, (result == true) ? "COMMIT" : "ROLLBACK") );
  353. return result;
  354. }
  355. /// update an existing account with the provided new data (both account and regs)
  356. static bool account_db_sql_save(AccountDB* self, const struct mmo_account* acc)
  357. {
  358. AccountDB_SQL* db = (AccountDB_SQL*)self;
  359. return mmo_auth_tosql(db, acc, false);
  360. }
  361. /// retrieve data from db and store it in the provided data structure
  362. static bool account_db_sql_load_num(AccountDB* self, struct mmo_account* acc, const int account_id)
  363. {
  364. AccountDB_SQL* db = (AccountDB_SQL*)self;
  365. return mmo_auth_fromsql(db, acc, account_id);
  366. }
  367. /// retrieve data from db and store it in the provided data structure
  368. static bool account_db_sql_load_str(AccountDB* self, struct mmo_account* acc, const char* userid)
  369. {
  370. AccountDB_SQL* db = (AccountDB_SQL*)self;
  371. Sql* sql_handle = db->accounts;
  372. char esc_userid[2*NAME_LENGTH+1];
  373. int account_id;
  374. char* data;
  375. Sql_EscapeString(sql_handle, esc_userid, userid);
  376. // get the list of account IDs for this user ID
  377. if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `account_id` FROM `%s` WHERE `userid`= %s '%s'",
  378. db->account_db, (db->case_sensitive ? "BINARY" : ""), esc_userid) )
  379. {
  380. Sql_ShowDebug(sql_handle);
  381. return false;
  382. }
  383. if( Sql_NumRows(sql_handle) > 1 )
  384. {// serious problem - duplicit account
  385. ShowError("account_db_sql_load_str: multiple accounts found when retrieving data for account '%s'!\n", userid);
  386. Sql_FreeResult(sql_handle);
  387. return false;
  388. }
  389. if( SQL_SUCCESS != Sql_NextRow(sql_handle) )
  390. {// no such entry
  391. Sql_FreeResult(sql_handle);
  392. return false;
  393. }
  394. Sql_GetData(sql_handle, 0, &data, NULL);
  395. account_id = atoi(data);
  396. return account_db_sql_load_num(self, acc, account_id);
  397. }
  398. /// Returns a new forward iterator.
  399. static AccountDBIterator* account_db_sql_iterator(AccountDB* self)
  400. {
  401. AccountDB_SQL* db = (AccountDB_SQL*)self;
  402. AccountDBIterator_SQL* iter = (AccountDBIterator_SQL*)aCalloc(1, sizeof(AccountDBIterator_SQL));
  403. // set up the vtable
  404. iter->vtable.destroy = &account_db_sql_iter_destroy;
  405. iter->vtable.next = &account_db_sql_iter_next;
  406. // fill data
  407. iter->db = db;
  408. iter->last_account_id = -1;
  409. return &iter->vtable;
  410. }
  411. /// Destroys this iterator, releasing all allocated memory (including itself).
  412. static void account_db_sql_iter_destroy(AccountDBIterator* self)
  413. {
  414. AccountDBIterator_SQL* iter = (AccountDBIterator_SQL*)self;
  415. aFree(iter);
  416. }
  417. /// Fetches the next account in the database.
  418. static bool account_db_sql_iter_next(AccountDBIterator* self, struct mmo_account* acc)
  419. {
  420. AccountDBIterator_SQL* iter = (AccountDBIterator_SQL*)self;
  421. AccountDB_SQL* db = (AccountDB_SQL*)iter->db;
  422. Sql* sql_handle = db->accounts;
  423. char* data;
  424. // get next account ID
  425. if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `account_id` FROM `%s` WHERE `account_id` > '%d' ORDER BY `account_id` ASC LIMIT 1",
  426. db->account_db, iter->last_account_id) )
  427. {
  428. Sql_ShowDebug(sql_handle);
  429. return false;
  430. }
  431. if( SQL_SUCCESS == Sql_NextRow(sql_handle) &&
  432. SQL_SUCCESS == Sql_GetData(sql_handle, 0, &data, NULL) &&
  433. data != NULL )
  434. {// get account data
  435. int account_id;
  436. account_id = atoi(data);
  437. if( mmo_auth_fromsql(db, acc, account_id) )
  438. {
  439. iter->last_account_id = account_id;
  440. Sql_FreeResult(sql_handle);
  441. return true;
  442. }
  443. }
  444. Sql_FreeResult(sql_handle);
  445. return false;
  446. }
  447. static bool mmo_auth_fromsql(AccountDB_SQL* db, struct mmo_account* acc, int account_id)
  448. {
  449. Sql* sql_handle = db->accounts;
  450. char* data;
  451. int i = 0;
  452. // retrieve login entry for the specified account
  453. if( SQL_ERROR == Sql_Query(sql_handle,
  454. "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",
  455. db->account_db, account_id )
  456. ) {
  457. Sql_ShowDebug(sql_handle);
  458. return false;
  459. }
  460. if( SQL_SUCCESS != Sql_NextRow(sql_handle) )
  461. {// no such entry
  462. Sql_FreeResult(sql_handle);
  463. return false;
  464. }
  465. Sql_GetData(sql_handle, 0, &data, NULL); acc->account_id = atoi(data);
  466. Sql_GetData(sql_handle, 1, &data, NULL); safestrncpy(acc->userid, data, sizeof(acc->userid));
  467. Sql_GetData(sql_handle, 2, &data, NULL); safestrncpy(acc->pass, data, sizeof(acc->pass));
  468. Sql_GetData(sql_handle, 3, &data, NULL); acc->sex = data[0];
  469. Sql_GetData(sql_handle, 4, &data, NULL); safestrncpy(acc->email, data, sizeof(acc->email));
  470. Sql_GetData(sql_handle, 5, &data, NULL); acc->group_id = atoi(data);
  471. Sql_GetData(sql_handle, 6, &data, NULL); acc->state = strtoul(data, NULL, 10);
  472. Sql_GetData(sql_handle, 7, &data, NULL); acc->unban_time = atol(data);
  473. Sql_GetData(sql_handle, 8, &data, NULL); acc->expiration_time = atol(data);
  474. Sql_GetData(sql_handle, 9, &data, NULL); acc->logincount = strtoul(data, NULL, 10);
  475. Sql_GetData(sql_handle, 10, &data, NULL); safestrncpy(acc->lastlogin, data, sizeof(acc->lastlogin));
  476. Sql_GetData(sql_handle, 11, &data, NULL); safestrncpy(acc->last_ip, data, sizeof(acc->last_ip));
  477. Sql_GetData(sql_handle, 12, &data, NULL); safestrncpy(acc->birthdate, data, sizeof(acc->birthdate));
  478. Sql_GetData(sql_handle, 13, &data, NULL); acc->char_slots = atoi(data);
  479. Sql_GetData(sql_handle, 14, &data, NULL); safestrncpy(acc->pincode, data, sizeof(acc->pincode));
  480. Sql_GetData(sql_handle, 15, &data, NULL); acc->pincode_change = atol(data);
  481. Sql_FreeResult(sql_handle);
  482. // retrieve account regs for the specified user
  483. if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `str`,`value` FROM `%s` WHERE `type`='1' AND `account_id`='%d'", db->accreg_db, acc->account_id) )
  484. {
  485. Sql_ShowDebug(sql_handle);
  486. return false;
  487. }
  488. acc->account_reg2_num = (int)Sql_NumRows(sql_handle);
  489. while( SQL_SUCCESS == Sql_NextRow(sql_handle) )
  490. {
  491. char* data;
  492. Sql_GetData(sql_handle, 0, &data, NULL); safestrncpy(acc->account_reg2[i].str, data, sizeof(acc->account_reg2[i].str));
  493. Sql_GetData(sql_handle, 1, &data, NULL); safestrncpy(acc->account_reg2[i].value, data, sizeof(acc->account_reg2[i].value));
  494. ++i;
  495. }
  496. Sql_FreeResult(sql_handle);
  497. if( i != acc->account_reg2_num )
  498. return false;
  499. return true;
  500. }
  501. static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, bool is_new)
  502. {
  503. Sql* sql_handle = db->accounts;
  504. SqlStmt* stmt = SqlStmt_Malloc(sql_handle);
  505. bool result = false;
  506. int i;
  507. // try
  508. do
  509. {
  510. if( SQL_SUCCESS != Sql_QueryStr(sql_handle, "START TRANSACTION") )
  511. {
  512. Sql_ShowDebug(sql_handle);
  513. break;
  514. }
  515. if( is_new )
  516. {// insert into account table
  517. if( SQL_SUCCESS != SqlStmt_Prepare(stmt,
  518. "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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
  519. db->account_db)
  520. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_INT, (void*)&acc->account_id, sizeof(acc->account_id))
  521. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_STRING, (void*)acc->userid, strlen(acc->userid))
  522. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 2, SQLDT_STRING, (void*)acc->pass, strlen(acc->pass))
  523. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 3, SQLDT_ENUM, (void*)&acc->sex, sizeof(acc->sex))
  524. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 4, SQLDT_STRING, (void*)&acc->email, strlen(acc->email))
  525. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 5, SQLDT_INT, (void*)&acc->group_id, sizeof(acc->group_id))
  526. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 6, SQLDT_UINT, (void*)&acc->state, sizeof(acc->state))
  527. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 7, SQLDT_LONG, (void*)&acc->unban_time, sizeof(acc->unban_time))
  528. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 8, SQLDT_INT, (void*)&acc->expiration_time, sizeof(acc->expiration_time))
  529. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 9, SQLDT_UINT, (void*)&acc->logincount, sizeof(acc->logincount))
  530. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 10, SQLDT_STRING, (void*)&acc->lastlogin, strlen(acc->lastlogin))
  531. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 11, SQLDT_STRING, (void*)&acc->last_ip, strlen(acc->last_ip))
  532. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 12, SQLDT_STRING, (void*)&acc->birthdate, strlen(acc->birthdate))
  533. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 13, SQLDT_UCHAR, (void*)&acc->char_slots, sizeof(acc->char_slots))
  534. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 14, SQLDT_STRING, (void*)&acc->pincode, strlen(acc->pincode))
  535. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 15, SQLDT_LONG, (void*)&acc->pincode_change, sizeof(acc->pincode_change))
  536. || SQL_SUCCESS != SqlStmt_Execute(stmt)
  537. ) {
  538. SqlStmt_ShowDebug(stmt);
  539. break;
  540. }
  541. }
  542. else
  543. {// update account table
  544. 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)
  545. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_STRING, (void*)acc->userid, strlen(acc->userid))
  546. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_STRING, (void*)acc->pass, strlen(acc->pass))
  547. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 2, SQLDT_ENUM, (void*)&acc->sex, sizeof(acc->sex))
  548. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 3, SQLDT_STRING, (void*)acc->email, strlen(acc->email))
  549. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 4, SQLDT_INT, (void*)&acc->group_id, sizeof(acc->group_id))
  550. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 5, SQLDT_UINT, (void*)&acc->state, sizeof(acc->state))
  551. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 6, SQLDT_LONG, (void*)&acc->unban_time, sizeof(acc->unban_time))
  552. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 7, SQLDT_LONG, (void*)&acc->expiration_time, sizeof(acc->expiration_time))
  553. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 8, SQLDT_UINT, (void*)&acc->logincount, sizeof(acc->logincount))
  554. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 9, SQLDT_STRING, (void*)&acc->lastlogin, strlen(acc->lastlogin))
  555. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 10, SQLDT_STRING, (void*)&acc->last_ip, strlen(acc->last_ip))
  556. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 11, SQLDT_STRING, (void*)&acc->birthdate, strlen(acc->birthdate))
  557. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 12, SQLDT_UCHAR, (void*)&acc->char_slots, sizeof(acc->char_slots))
  558. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 13, SQLDT_STRING, (void*)&acc->pincode, strlen(acc->pincode))
  559. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 14, SQLDT_LONG, (void*)&acc->pincode_change, sizeof(acc->pincode_change))
  560. || SQL_SUCCESS != SqlStmt_Execute(stmt)
  561. ) {
  562. SqlStmt_ShowDebug(stmt);
  563. break;
  564. }
  565. }
  566. // remove old account regs
  567. if( SQL_SUCCESS != Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `type`='1' AND `account_id`='%d'", db->accreg_db, acc->account_id) )
  568. {
  569. Sql_ShowDebug(sql_handle);
  570. break;
  571. }
  572. // insert new account regs
  573. if( SQL_SUCCESS != SqlStmt_Prepare(stmt, "INSERT INTO `%s` (`type`, `account_id`, `str`, `value`) VALUES ( 1 , '%d' , ? , ? );", db->accreg_db, acc->account_id) )
  574. {
  575. SqlStmt_ShowDebug(stmt);
  576. break;
  577. }
  578. for( i = 0; i < acc->account_reg2_num; ++i )
  579. {
  580. if( SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_STRING, (void*)acc->account_reg2[i].str, strlen(acc->account_reg2[i].str))
  581. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_STRING, (void*)acc->account_reg2[i].value, strlen(acc->account_reg2[i].value))
  582. || SQL_SUCCESS != SqlStmt_Execute(stmt)
  583. ) {
  584. SqlStmt_ShowDebug(stmt);
  585. break;
  586. }
  587. }
  588. if( i < acc->account_reg2_num )
  589. {
  590. result = false;
  591. break;
  592. }
  593. // if we got this far, everything was successful
  594. result = true;
  595. } while(0);
  596. // finally
  597. result &= ( SQL_SUCCESS == Sql_QueryStr(sql_handle, (result == true) ? "COMMIT" : "ROLLBACK") );
  598. SqlStmt_Free(stmt);
  599. return result;
  600. }