int_mail.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699
  1. // Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
  2. // For more information, see LICENCE in the main folder
  3. #include "int_mail.hpp"
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include "../common/mmo.hpp"
  7. #include "../common/showmsg.hpp"
  8. #include "../common/socket.hpp"
  9. #include "../common/sql.hpp"
  10. #include "../common/strlib.hpp"
  11. #include "char.hpp"
  12. #include "char_mapif.hpp"
  13. #include "inter.hpp"
  14. bool mail_loadmessage(int mail_id, struct mail_message* msg);
  15. void mapif_Mail_return(int fd, uint32 char_id, int mail_id);
  16. void mapif_Mail_delete(int fd, uint32 char_id, int mail_id, bool deleted);
  17. int mail_fromsql(uint32 char_id, struct mail_data* md)
  18. {
  19. int i;
  20. char *data;
  21. memset(md, 0, sizeof(struct mail_data));
  22. md->amount = 0;
  23. md->full = false;
  24. // First we prefill the msg ids
  25. if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `id` FROM `%s` WHERE `dest_id`='%d' AND `status` < 3 ORDER BY `id` LIMIT %d", schema_config.mail_db, char_id, MAIL_MAX_INBOX + 1) ){
  26. Sql_ShowDebug(sql_handle);
  27. return 0;
  28. }
  29. md->full = (Sql_NumRows(sql_handle) > MAIL_MAX_INBOX);
  30. for( i = 0; i < MAIL_MAX_INBOX && SQL_SUCCESS == Sql_NextRow(sql_handle); i++ ){
  31. Sql_GetData(sql_handle, 0, &data, NULL); md->msg[i].id = atoi(data);
  32. }
  33. md->amount = i;
  34. Sql_FreeResult(sql_handle);
  35. // Now we load them
  36. for( i = 0; i < md->amount; i++ ){
  37. if( !mail_loadmessage( md->msg[i].id, &md->msg[i] ) ){
  38. return 0;
  39. }
  40. }
  41. md->unchecked = 0;
  42. md->unread = 0;
  43. for (i = 0; i < md->amount; i++)
  44. {
  45. struct mail_message *msg = &md->msg[i];
  46. if( msg->status == MAIL_NEW )
  47. {
  48. if ( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `status` = '%d' WHERE `id` = '%d'", schema_config.mail_db, MAIL_UNREAD, msg->id) )
  49. Sql_ShowDebug(sql_handle);
  50. msg->status = MAIL_UNREAD;
  51. md->unchecked++;
  52. }
  53. else if ( msg->status == MAIL_UNREAD )
  54. md->unread++;
  55. }
  56. ShowInfo("mail load complete from DB - id: %d (total: %d)\n", char_id, md->amount);
  57. return 1;
  58. }
  59. /// Stores a single message in the database.
  60. /// Returns the message's ID if successful (or 0 if it fails).
  61. int mail_savemessage(struct mail_message* msg)
  62. {
  63. StringBuf buf;
  64. SqlStmt* stmt;
  65. int i, j;
  66. bool found = false;
  67. // build message save query
  68. StringBuf_Init(&buf);
  69. StringBuf_Printf(&buf, "INSERT INTO `%s` (`send_name`, `send_id`, `dest_name`, `dest_id`, `title`, `message`, `time`, `status`, `zeny`,`type`", schema_config.mail_db);
  70. StringBuf_Printf(&buf, ") VALUES (?, '%d', ?, '%d', ?, ?, '%lu', '%d', '%d', '%d'", msg->send_id, msg->dest_id, (unsigned long)msg->timestamp, msg->status, msg->zeny, msg->type);
  71. StringBuf_AppendStr(&buf, ")");
  72. // prepare and execute query
  73. stmt = SqlStmt_Malloc(sql_handle);
  74. if( SQL_SUCCESS != SqlStmt_PrepareStr(stmt, StringBuf_Value(&buf))
  75. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_STRING, msg->send_name, strnlen(msg->send_name, NAME_LENGTH))
  76. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_STRING, msg->dest_name, strnlen(msg->dest_name, NAME_LENGTH))
  77. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 2, SQLDT_STRING, msg->title, strnlen(msg->title, MAIL_TITLE_LENGTH))
  78. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 3, SQLDT_STRING, msg->body, strnlen(msg->body, MAIL_BODY_LENGTH))
  79. || SQL_SUCCESS != SqlStmt_Execute(stmt) )
  80. {
  81. SqlStmt_ShowDebug(stmt);
  82. StringBuf_Destroy(&buf);
  83. return msg->id = 0;
  84. } else
  85. msg->id = (int)SqlStmt_LastInsertId(stmt);
  86. SqlStmt_Free(stmt);
  87. StringBuf_Clear(&buf);
  88. StringBuf_Printf(&buf,"INSERT INTO `%s` (`id`, `index`, `amount`, `nameid`, `refine`, `attribute`, `identify`, `unique_id`, `bound`, `enchantgrade`", schema_config.mail_attachment_db);
  89. for (j = 0; j < MAX_SLOTS; j++)
  90. StringBuf_Printf(&buf, ", `card%d`", j);
  91. for (j = 0; j < MAX_ITEM_RDM_OPT; ++j) {
  92. StringBuf_Printf(&buf, ", `option_id%d`", j);
  93. StringBuf_Printf(&buf, ", `option_val%d`", j);
  94. StringBuf_Printf(&buf, ", `option_parm%d`", j);
  95. }
  96. StringBuf_AppendStr(&buf, ") VALUES ");
  97. for( i = 0; i < MAIL_MAX_ITEM; i++ ){
  98. // skip empty and already matched entries
  99. if( msg->item[i].nameid == 0 )
  100. continue;
  101. if( found ){
  102. StringBuf_AppendStr(&buf, ",");
  103. }else{
  104. found = true;
  105. }
  106. StringBuf_Printf(&buf, "('%" PRIu64 "', '%hu', '%d', '%u', '%d', '%d', '%d', '%" PRIu64 "', '%d', '%d'", (uint64)msg->id, i, msg->item[i].amount, msg->item[i].nameid, msg->item[i].refine, msg->item[i].attribute, msg->item[i].identify, msg->item[i].unique_id, msg->item[i].bound, msg->item[i].enchantgrade);
  107. for (j = 0; j < MAX_SLOTS; j++)
  108. StringBuf_Printf(&buf, ", '%u'", msg->item[i].card[j]);
  109. for (j = 0; j < MAX_ITEM_RDM_OPT; ++j) {
  110. StringBuf_Printf(&buf, ", '%d'", msg->item[i].option[j].id);
  111. StringBuf_Printf(&buf, ", '%d'", msg->item[i].option[j].value);
  112. StringBuf_Printf(&buf, ", '%d'", msg->item[i].option[j].param);
  113. }
  114. StringBuf_AppendStr(&buf, ")");
  115. }
  116. if( found && SQL_ERROR == Sql_QueryStr(sql_handle, StringBuf_Value(&buf)) ){
  117. Sql_ShowDebug(sql_handle);
  118. }
  119. StringBuf_Destroy(&buf);
  120. return msg->id;
  121. }
  122. /// Retrieves a single message from the database.
  123. /// Returns true if the operation succeeds (or false if it fails).
  124. bool mail_loadmessage(int mail_id, struct mail_message* msg)
  125. {
  126. int i, j;
  127. StringBuf buf;
  128. char* data;
  129. if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `id`,`send_name`,`send_id`,`dest_name`,`dest_id`,`title`,`message`,`time`,`status`,`zeny`,`type` FROM `%s` WHERE `id` = '%d'", schema_config.mail_db, mail_id )
  130. || SQL_SUCCESS != Sql_NextRow(sql_handle) ){
  131. Sql_ShowDebug(sql_handle);
  132. Sql_FreeResult(sql_handle);
  133. return false;
  134. }else{
  135. Sql_GetData(sql_handle, 0, &data, NULL); msg->id = atoi(data);
  136. Sql_GetData(sql_handle, 1, &data, NULL); safestrncpy(msg->send_name, data, NAME_LENGTH);
  137. Sql_GetData(sql_handle, 2, &data, NULL); msg->send_id = atoi(data);
  138. Sql_GetData(sql_handle, 3, &data, NULL); safestrncpy(msg->dest_name, data, NAME_LENGTH);
  139. Sql_GetData(sql_handle, 4, &data, NULL); msg->dest_id = atoi(data);
  140. Sql_GetData(sql_handle, 5, &data, NULL); safestrncpy(msg->title, data, MAIL_TITLE_LENGTH);
  141. Sql_GetData(sql_handle, 6, &data, NULL); safestrncpy(msg->body, data, MAIL_BODY_LENGTH);
  142. Sql_GetData(sql_handle, 7, &data, NULL); msg->timestamp = atoi(data);
  143. Sql_GetData(sql_handle, 8, &data, NULL); msg->status = (mail_status)atoi(data);
  144. Sql_GetData(sql_handle, 9, &data, NULL); msg->zeny = atoi(data);
  145. Sql_GetData(sql_handle,10, &data, NULL); msg->type = (mail_inbox_type)atoi(data);
  146. if( msg->type == MAIL_INBOX_NORMAL && charserv_config.mail_return_days > 0 ){
  147. msg->scheduled_deletion = msg->timestamp + charserv_config.mail_return_days * 24 * 60 * 60;
  148. }else if( msg->type == MAIL_INBOX_RETURNED && charserv_config.mail_delete_days > 0 ){
  149. msg->scheduled_deletion = msg->timestamp + charserv_config.mail_delete_days * 24 * 60 * 60;
  150. }else{
  151. msg->scheduled_deletion = 0;
  152. }
  153. Sql_FreeResult(sql_handle);
  154. }
  155. StringBuf_Init(&buf);
  156. StringBuf_AppendStr(&buf, "SELECT `amount`,`nameid`,`refine`,`attribute`,`identify`,`unique_id`,`bound`,`enchantgrade`");
  157. for (j = 0; j < MAX_SLOTS; j++)
  158. StringBuf_Printf(&buf, ",`card%d`", j);
  159. for (j = 0; j < MAX_ITEM_RDM_OPT; ++j) {
  160. StringBuf_Printf(&buf, ", `option_id%d`", j);
  161. StringBuf_Printf(&buf, ", `option_val%d`", j);
  162. StringBuf_Printf(&buf, ", `option_parm%d`", j);
  163. }
  164. StringBuf_Printf(&buf, " FROM `%s`", schema_config.mail_attachment_db);
  165. StringBuf_Printf(&buf, " WHERE `id` = '%d'", mail_id);
  166. StringBuf_AppendStr(&buf, " ORDER BY `index` ASC");
  167. StringBuf_Printf(&buf, " LIMIT %d", MAIL_MAX_ITEM);
  168. if( SQL_ERROR == Sql_Query(sql_handle, StringBuf_Value(&buf)) ){
  169. Sql_ShowDebug(sql_handle);
  170. Sql_FreeResult(sql_handle);
  171. StringBuf_Destroy(&buf);
  172. return false;
  173. }
  174. memset(msg->item, 0, sizeof(struct item) * MAIL_MAX_ITEM);
  175. for( i = 0; i < MAIL_MAX_ITEM && SQL_SUCCESS == Sql_NextRow(sql_handle); i++ ){
  176. Sql_GetData(sql_handle,0, &data, NULL); msg->item[i].amount = (short)atoi(data);
  177. Sql_GetData(sql_handle,1, &data, NULL); msg->item[i].nameid = strtoul(data, nullptr, 10);
  178. Sql_GetData(sql_handle,2, &data, NULL); msg->item[i].refine = atoi(data);
  179. Sql_GetData(sql_handle,3, &data, NULL); msg->item[i].attribute = atoi(data);
  180. Sql_GetData(sql_handle,4, &data, NULL); msg->item[i].identify = atoi(data);
  181. Sql_GetData(sql_handle,5, &data, NULL); msg->item[i].unique_id = strtoull(data, NULL, 10);
  182. Sql_GetData(sql_handle,6, &data, NULL); msg->item[i].bound = atoi(data);
  183. Sql_GetData(sql_handle,7, &data, NULL); msg->item[i].enchantgrade = atoi(data);
  184. msg->item[i].expire_time = 0;
  185. for( j = 0; j < MAX_SLOTS; j++ ){
  186. Sql_GetData(sql_handle,8 + j, &data, NULL); msg->item[i].card[j] = strtoul(data, nullptr, 10);
  187. }
  188. for( j = 0; j < MAX_ITEM_RDM_OPT; j++ ){
  189. Sql_GetData(sql_handle, 8 + MAX_SLOTS + j * 3, &data, NULL); msg->item[i].option[j].id = atoi(data);
  190. Sql_GetData(sql_handle, 9 + MAX_SLOTS + j * 3, &data, NULL); msg->item[i].option[j].value = atoi(data);
  191. Sql_GetData(sql_handle,10 + MAX_SLOTS + j * 3, &data, NULL); msg->item[i].option[j].param = atoi(data);
  192. }
  193. }
  194. StringBuf_Destroy(&buf);
  195. Sql_FreeResult(sql_handle);
  196. return true;
  197. }
  198. int mail_timer_sub( int limit, enum mail_inbox_type type ){
  199. struct{
  200. int mail_id;
  201. int char_id;
  202. int account_id;
  203. }mails[MAIL_MAX_INBOX];
  204. int i, map_fd;
  205. char* data;
  206. struct online_char_data* character;
  207. if( limit <= 0 ){
  208. return 0;
  209. }
  210. memset(mails, 0, sizeof(mails));
  211. if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `id`,`char_id`,`account_id` FROM `%s` `m` INNER JOIN `%s` `c` ON `c`.`char_id`=`m`.`dest_id` WHERE `type` = '%d' AND `time` <= UNIX_TIMESTAMP( NOW() - INTERVAL %d DAY ) ORDER BY `id` LIMIT %d", schema_config.mail_db, schema_config.char_db, type, limit, MAIL_MAX_INBOX + 1) ){
  212. Sql_ShowDebug(sql_handle);
  213. return 0;
  214. }
  215. if( Sql_NumRows(sql_handle) <= 0 ){
  216. return 0;
  217. }
  218. for( i = 0; i < MAIL_MAX_INBOX && SQL_SUCCESS == Sql_NextRow(sql_handle); i++ ){
  219. Sql_GetData(sql_handle, 0, &data, NULL); mails[i].mail_id = atoi(data);
  220. Sql_GetData(sql_handle, 1, &data, NULL); mails[i].char_id = atoi(data);
  221. Sql_GetData(sql_handle, 2, &data, NULL); mails[i].account_id = atoi(data);
  222. }
  223. Sql_FreeResult(sql_handle);
  224. for( i = 0; i < MAIL_MAX_INBOX; i++ ){
  225. if( mails[i].mail_id == 0 ){
  226. break;
  227. }
  228. // Check for online players
  229. if( ( character = (struct online_char_data*)idb_get(char_get_onlinedb(), mails[i].account_id) ) != NULL && character->server >= 0 ){
  230. map_fd = map_server[character->server].fd;
  231. }else{
  232. map_fd = 0;
  233. }
  234. if( type == MAIL_INBOX_NORMAL ){
  235. mapif_Mail_return( 0, mails[i].char_id, mails[i].mail_id );
  236. mapif_Mail_delete( map_fd, mails[i].char_id, mails[i].mail_id, true );
  237. }else if( type == MAIL_INBOX_RETURNED ){
  238. mapif_Mail_delete( map_fd, mails[i].char_id, mails[i].mail_id, false );
  239. }else{
  240. // Should not happen
  241. continue;
  242. }
  243. }
  244. return 0;
  245. }
  246. TIMER_FUNC(mail_return_timer){
  247. return mail_timer_sub( charserv_config.mail_return_days, MAIL_INBOX_NORMAL );
  248. }
  249. TIMER_FUNC(mail_delete_timer){
  250. return mail_timer_sub( charserv_config.mail_delete_days, MAIL_INBOX_RETURNED );
  251. }
  252. /*==========================================
  253. * Client Inbox Request
  254. *------------------------------------------*/
  255. void mapif_Mail_sendinbox(int fd, uint32 char_id, unsigned char flag, enum mail_inbox_type type)
  256. {
  257. struct mail_data md;
  258. mail_fromsql(char_id, &md);
  259. //FIXME: dumping the whole structure like this is unsafe [ultramage]
  260. WFIFOHEAD(fd, sizeof(md) + 10);
  261. WFIFOW(fd,0) = 0x3848;
  262. WFIFOW(fd,2) = sizeof(md) + 10;
  263. WFIFOL(fd,4) = char_id;
  264. WFIFOB(fd,8) = flag;
  265. WFIFOB(fd,9) = type;
  266. memcpy(WFIFOP(fd,10),&md,sizeof(md));
  267. WFIFOSET(fd,WFIFOW(fd,2));
  268. }
  269. void mapif_parse_Mail_requestinbox(int fd)
  270. {
  271. mapif_Mail_sendinbox(fd, RFIFOL(fd,2), RFIFOB(fd,6), (mail_inbox_type)RFIFOB(fd,7));
  272. }
  273. /*==========================================
  274. * Mark mail as 'Read'
  275. *------------------------------------------*/
  276. void mapif_parse_Mail_read(int fd)
  277. {
  278. int mail_id = RFIFOL(fd,2);
  279. if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `status` = '%d' WHERE `id` = '%d'", schema_config.mail_db, MAIL_READ, mail_id) )
  280. Sql_ShowDebug(sql_handle);
  281. }
  282. /*==========================================
  283. * Client Attachment Request
  284. *------------------------------------------*/
  285. bool mail_DeleteAttach(int mail_id){
  286. if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `id` = '%d'", schema_config.mail_attachment_db, mail_id ) ){
  287. Sql_ShowDebug(sql_handle);
  288. return false;
  289. }
  290. return true;
  291. }
  292. void mapif_Mail_getattach(int fd, uint32 char_id, int mail_id, int type)
  293. {
  294. struct mail_message msg;
  295. if( ( type&MAIL_ATT_ALL ) == 0 ){
  296. return;
  297. }
  298. if( !mail_loadmessage(mail_id, &msg) )
  299. return;
  300. if( msg.dest_id != char_id )
  301. return;
  302. if( charserv_config.mail_retrieve == 0 && msg.status != MAIL_READ )
  303. return;
  304. if( type & MAIL_ATT_ZENY ){
  305. if( msg.zeny > 0 ){
  306. if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `zeny` = 0 WHERE `id` = '%d'", schema_config.mail_db, mail_id ) ){
  307. Sql_ShowDebug(sql_handle);
  308. return;
  309. }
  310. }else{
  311. type &= ~MAIL_ATT_ZENY;
  312. }
  313. }
  314. if( type & MAIL_ATT_ITEM ){
  315. int i;
  316. ARR_FIND(0, MAIL_MAX_ITEM, i, msg.item[i].nameid > 0 && msg.item[i].amount > 0);
  317. // No item was found
  318. if( i == MAIL_MAX_ITEM ){
  319. type &= ~MAIL_ATT_ITEM;
  320. }else{
  321. if( !mail_DeleteAttach(mail_id) ){
  322. return;
  323. }
  324. }
  325. }
  326. if( type == MAIL_ATT_NONE )
  327. return; // No Attachment
  328. WFIFOHEAD(fd, sizeof(struct item)*MAIL_MAX_ITEM + 16);
  329. WFIFOW(fd,0) = 0x384a;
  330. WFIFOW(fd,2) = sizeof(struct item)*MAIL_MAX_ITEM + 16;
  331. WFIFOL(fd,4) = char_id;
  332. WFIFOL(fd,8) = mail_id;
  333. if( type & MAIL_ATT_ZENY ){
  334. WFIFOL(fd,12) = msg.zeny;
  335. }else{
  336. WFIFOL(fd, 12) = 0;
  337. }
  338. if( type & MAIL_ATT_ITEM ){
  339. memcpy(WFIFOP(fd, 16), &msg.item, sizeof(struct item)*MAIL_MAX_ITEM);
  340. }else{
  341. memset(WFIFOP(fd, 16), 0, sizeof(struct item)*MAIL_MAX_ITEM);
  342. }
  343. WFIFOSET(fd,WFIFOW(fd,2));
  344. }
  345. void mapif_parse_Mail_getattach(int fd)
  346. {
  347. mapif_Mail_getattach(fd, RFIFOL(fd,2), RFIFOL(fd,6),RFIFOB(fd,10));
  348. }
  349. /*==========================================
  350. * Delete Mail
  351. *------------------------------------------*/
  352. void mapif_Mail_delete(int fd, uint32 char_id, int mail_id, bool deleted)
  353. {
  354. bool failed = false;
  355. if( !deleted ){
  356. if ( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `id` = '%d'", schema_config.mail_db, mail_id) ||
  357. SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `id` = '%d'", schema_config.mail_attachment_db, mail_id) )
  358. {
  359. Sql_ShowDebug(sql_handle);
  360. failed = true;
  361. }
  362. }
  363. // Only if the request came from a map-server and was not timer triggered for an offline character
  364. if( fd <= 0 ){
  365. return;
  366. }
  367. WFIFOHEAD(fd,11);
  368. WFIFOW(fd,0) = 0x384b;
  369. WFIFOL(fd,2) = char_id;
  370. WFIFOL(fd,6) = mail_id;
  371. WFIFOB(fd,10) = failed;
  372. WFIFOSET(fd,11);
  373. }
  374. void mapif_parse_Mail_delete(int fd)
  375. {
  376. mapif_Mail_delete(fd, RFIFOL(fd,2), RFIFOL(fd,6), false);
  377. }
  378. /*==========================================
  379. * Report New Mail to Map Server
  380. *------------------------------------------*/
  381. void mapif_Mail_new(struct mail_message *msg)
  382. {
  383. unsigned char buf[75];
  384. if( !msg || !msg->id )
  385. return;
  386. WBUFW(buf,0) = 0x3849;
  387. WBUFL(buf,2) = msg->dest_id;
  388. WBUFL(buf,6) = msg->id;
  389. memcpy(WBUFP(buf,10), msg->send_name, NAME_LENGTH);
  390. memcpy(WBUFP(buf,34), msg->title, MAIL_TITLE_LENGTH);
  391. WBUFB(buf,74) = msg->type;
  392. chmapif_sendall(buf, 75);
  393. }
  394. /*==========================================
  395. * Return Mail
  396. *------------------------------------------*/
  397. void mapif_Mail_return(int fd, uint32 char_id, int mail_id)
  398. {
  399. struct mail_message msg;
  400. int new_mail = 0;
  401. if( mail_loadmessage(mail_id, &msg) )
  402. {
  403. if( msg.dest_id != char_id)
  404. return;
  405. else if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `id` = '%d'", schema_config.mail_db, mail_id)
  406. || SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `id` = '%d'", schema_config.mail_attachment_db, mail_id) )
  407. Sql_ShowDebug(sql_handle);
  408. // If it was not sent by the server, since we do not want to return mails to the server
  409. else if( msg.send_id != 0 )
  410. {
  411. char temp_[MAIL_TITLE_LENGTH + 3];
  412. // swap sender and receiver
  413. SWAP(msg.send_id, msg.dest_id);
  414. safestrncpy(temp_, msg.send_name, NAME_LENGTH);
  415. safestrncpy(msg.send_name, msg.dest_name, NAME_LENGTH);
  416. safestrncpy(msg.dest_name, temp_, NAME_LENGTH);
  417. // set reply message title
  418. snprintf(temp_, sizeof(temp_), "RE:%s", msg.title);
  419. safestrncpy(msg.title, temp_, sizeof(temp_));
  420. msg.status = MAIL_NEW;
  421. msg.type = MAIL_INBOX_RETURNED;
  422. msg.timestamp = time(NULL);
  423. new_mail = mail_savemessage(&msg);
  424. mapif_Mail_new(&msg);
  425. }
  426. }
  427. // Only if the request came from a map-server and was not timer triggered for an offline character
  428. if( fd <= 0 ){
  429. return;
  430. }
  431. WFIFOHEAD(fd,11);
  432. WFIFOW(fd,0) = 0x384c;
  433. WFIFOL(fd,2) = char_id;
  434. WFIFOL(fd,6) = mail_id;
  435. WFIFOB(fd,10) = (new_mail == 0);
  436. WFIFOSET(fd,11);
  437. }
  438. void mapif_parse_Mail_return(int fd)
  439. {
  440. mapif_Mail_return(fd, RFIFOL(fd,2), RFIFOL(fd,6));
  441. }
  442. /*==========================================
  443. * Send Mail
  444. *------------------------------------------*/
  445. void mapif_Mail_send(int fd, struct mail_message* msg)
  446. {
  447. int len = sizeof(struct mail_message) + 4;
  448. WFIFOHEAD(fd,len);
  449. WFIFOW(fd,0) = 0x384d;
  450. WFIFOW(fd,2) = len;
  451. memcpy(WFIFOP(fd,4), msg, sizeof(struct mail_message));
  452. WFIFOSET(fd,len);
  453. }
  454. void mapif_parse_Mail_send(int fd)
  455. {
  456. struct mail_message msg;
  457. char esc_name[NAME_LENGTH*2+1];
  458. char *data;
  459. size_t len;
  460. if(RFIFOW(fd,2) != 8 + sizeof(struct mail_message))
  461. return;
  462. memcpy(&msg, RFIFOP(fd,8), sizeof(struct mail_message));
  463. if( msg.dest_id != 0 ){
  464. if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `char_id`, `name` FROM `%s` WHERE `char_id` = '%u'", schema_config.char_db, msg.dest_id) ){
  465. Sql_ShowDebug(sql_handle);
  466. return;
  467. }
  468. msg.dest_id = 0;
  469. msg.dest_name[0] = '\0';
  470. if( SQL_SUCCESS == Sql_NextRow(sql_handle) ){
  471. Sql_GetData(sql_handle, 0, &data, NULL);
  472. msg.dest_id = atoi(data);
  473. Sql_GetData(sql_handle, 1, &data, &len);
  474. safestrncpy(msg.dest_name, data, NAME_LENGTH);
  475. }
  476. Sql_FreeResult(sql_handle);
  477. }
  478. // Try to find the Dest Char by Name
  479. Sql_EscapeStringLen(sql_handle, esc_name, msg.dest_name, strnlen(msg.dest_name, NAME_LENGTH));
  480. if ( SQL_ERROR == Sql_Query(sql_handle, "SELECT `account_id`, `char_id` FROM `%s` WHERE `name` = '%s'", schema_config.char_db, esc_name) ){
  481. Sql_ShowDebug(sql_handle);
  482. }else if ( SQL_SUCCESS == Sql_NextRow(sql_handle) ){
  483. #if PACKETVER < 20150513
  484. uint32 account_id = RFIFOL(fd,4);
  485. Sql_GetData(sql_handle, 0, &data, NULL);
  486. if (atoi(data) != account_id)
  487. { // Cannot send mail to char in the same account
  488. Sql_GetData(sql_handle, 1, &data, NULL);
  489. msg.dest_id = atoi(data);
  490. }
  491. #else
  492. // In RODEX you can even send mails to yourself
  493. Sql_GetData(sql_handle, 1, &data, NULL);
  494. msg.dest_id = atoi(data);
  495. #endif
  496. }
  497. Sql_FreeResult(sql_handle);
  498. msg.status = MAIL_NEW;
  499. if( msg.dest_id > 0 )
  500. msg.id = mail_savemessage(&msg);
  501. mapif_Mail_send(fd, &msg); // notify sender
  502. mapif_Mail_new(&msg); // notify recipient
  503. }
  504. bool mail_sendmail(int send_id, const char* send_name, int dest_id, const char* dest_name, const char* title, const char* body, int zeny, struct item *item, int amount)
  505. {
  506. struct mail_message msg;
  507. memset(&msg, 0, sizeof(struct mail_message));
  508. msg.send_id = send_id;
  509. safestrncpy(msg.send_name, send_name, NAME_LENGTH);
  510. msg.dest_id = dest_id;
  511. safestrncpy(msg.dest_name, dest_name, NAME_LENGTH);
  512. safestrncpy(msg.title, title, MAIL_TITLE_LENGTH);
  513. safestrncpy(msg.body, body, MAIL_BODY_LENGTH);
  514. msg.zeny = zeny;
  515. if( item != NULL ){
  516. int i;
  517. for( i = 0; i < amount && i < MAIL_MAX_ITEM; i++ ){
  518. memcpy(&msg.item[i], &item[i], sizeof(struct item));
  519. }
  520. }
  521. msg.timestamp = time(NULL);
  522. msg.type = MAIL_INBOX_NORMAL;
  523. if( !mail_savemessage(&msg) ){
  524. return false;
  525. }
  526. mapif_Mail_new(&msg);
  527. return true;
  528. }
  529. void mapif_Mail_receiver_send( int fd, int requesting_char_id, int char_id, int class_, int base_level, const char* name ){
  530. WFIFOHEAD(fd,38);
  531. WFIFOW(fd,0) = 0x384e;
  532. WFIFOL(fd,2) = requesting_char_id;
  533. WFIFOL(fd,6) = char_id;
  534. WFIFOW(fd,10) = class_;
  535. WFIFOW(fd,12) = base_level;
  536. strncpy(WFIFOCP(fd, 14), name, NAME_LENGTH);
  537. WFIFOSET(fd,38);
  538. }
  539. void mapif_parse_Mail_receiver_check( int fd ){
  540. char name[NAME_LENGTH], esc_name[NAME_LENGTH * 2 + 1];
  541. uint32 char_id = 0;
  542. uint16 class_ = 0, base_level = 0;
  543. safestrncpy( name, RFIFOCP(fd, 6), NAME_LENGTH );
  544. // Try to find the Dest Char by Name
  545. Sql_EscapeStringLen( sql_handle, esc_name, name, strnlen( name, NAME_LENGTH ) );
  546. if( SQL_ERROR == Sql_Query( sql_handle, "SELECT `char_id`,`class`,`base_level` FROM `%s` WHERE `name` = '%s'", schema_config.char_db, esc_name ) ){
  547. Sql_ShowDebug(sql_handle);
  548. }else if( SQL_SUCCESS == Sql_NextRow(sql_handle) ){
  549. char *data;
  550. Sql_GetData(sql_handle, 0, &data, NULL); char_id = atoi(data);
  551. Sql_GetData(sql_handle, 1, &data, NULL); class_ = atoi(data);
  552. Sql_GetData(sql_handle, 2, &data, NULL); base_level = atoi(data);
  553. }
  554. Sql_FreeResult(sql_handle);
  555. mapif_Mail_receiver_send( fd, RFIFOL(fd, 2), char_id, class_, base_level, name );
  556. }
  557. /*==========================================
  558. * Packets From Map Server
  559. *------------------------------------------*/
  560. int inter_mail_parse_frommap(int fd)
  561. {
  562. switch(RFIFOW(fd,0))
  563. {
  564. case 0x3048: mapif_parse_Mail_requestinbox(fd); break;
  565. case 0x3049: mapif_parse_Mail_read(fd); break;
  566. case 0x304a: mapif_parse_Mail_getattach(fd); break;
  567. case 0x304b: mapif_parse_Mail_delete(fd); break;
  568. case 0x304c: mapif_parse_Mail_return(fd); break;
  569. case 0x304d: mapif_parse_Mail_send(fd); break;
  570. case 0x304e: mapif_parse_Mail_receiver_check(fd); break;
  571. default:
  572. return 0;
  573. }
  574. return 1;
  575. }
  576. int inter_mail_sql_init(void)
  577. {
  578. return 1;
  579. }
  580. void inter_mail_sql_final(void)
  581. {
  582. return;
  583. }