int_mail.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  1. // Copyright (c) Athena Dev Teams - Licensed under GNU GPL
  2. // For more information, see LICENCE in the main folder
  3. #include "../common/mmo.h"
  4. #include "../common/malloc.h"
  5. #include "../common/showmsg.h"
  6. #include "../common/socket.h"
  7. #include "../common/strlib.h"
  8. #include "../common/sql.h"
  9. #include "../common/timer.h"
  10. #include "char.h"
  11. #include "inter.h"
  12. #include <stdio.h>
  13. #include <string.h>
  14. #include <stdlib.h>
  15. static int mail_fromsql(int char_id, struct mail_data* md)
  16. {
  17. int i, j;
  18. struct mail_message *msg;
  19. struct item *item;
  20. char *data;
  21. StringBuf buf;
  22. memset(md, 0, sizeof(struct mail_data));
  23. md->amount = 0;
  24. md->full = false;
  25. StringBuf_Init(&buf);
  26. StringBuf_AppendStr(&buf, "SELECT `id`,`send_name`,`send_id`,`dest_name`,`dest_id`,`title`,`message`,`time`,`status`,"
  27. "`zeny`,`amount`,`nameid`,`refine`,`attribute`,`identify`");
  28. for (i = 0; i < MAX_SLOTS; i++)
  29. StringBuf_Printf(&buf, ",`card%d`", i);
  30. // I keep the `status` < 3 just in case someone forget to apply the sqlfix
  31. StringBuf_Printf(&buf, " FROM `%s` WHERE `dest_id`='%d' AND `status` < 3 ORDER BY `id` LIMIT %d",
  32. mail_db, char_id, MAIL_MAX_INBOX + 1);
  33. if( SQL_ERROR == Sql_Query(sql_handle, StringBuf_Value(&buf)) )
  34. Sql_ShowDebug(sql_handle);
  35. StringBuf_Destroy(&buf);
  36. for (i = 0; i < MAIL_MAX_INBOX && SQL_SUCCESS == Sql_NextRow(sql_handle); ++i )
  37. {
  38. msg = &md->msg[i];
  39. Sql_GetData(sql_handle, 0, &data, NULL); msg->id = atoi(data);
  40. Sql_GetData(sql_handle, 1, &data, NULL); safestrncpy(msg->send_name, data, NAME_LENGTH);
  41. Sql_GetData(sql_handle, 2, &data, NULL); msg->send_id = atoi(data);
  42. Sql_GetData(sql_handle, 3, &data, NULL); safestrncpy(msg->dest_name, data, NAME_LENGTH);
  43. Sql_GetData(sql_handle, 4, &data, NULL); msg->dest_id = atoi(data);
  44. Sql_GetData(sql_handle, 5, &data, NULL); safestrncpy(msg->title, data, MAIL_TITLE_LENGTH);
  45. Sql_GetData(sql_handle, 6, &data, NULL); safestrncpy(msg->body, data, MAIL_BODY_LENGTH);
  46. Sql_GetData(sql_handle, 7, &data, NULL); msg->timestamp = atoi(data);
  47. Sql_GetData(sql_handle, 8, &data, NULL); msg->status = (mail_status)atoi(data);
  48. Sql_GetData(sql_handle, 9, &data, NULL); msg->zeny = atoi(data);
  49. item = &msg->item;
  50. Sql_GetData(sql_handle,10, &data, NULL); item->amount = (short)atoi(data);
  51. Sql_GetData(sql_handle,11, &data, NULL); item->nameid = atoi(data);
  52. Sql_GetData(sql_handle,12, &data, NULL); item->refine = atoi(data);
  53. Sql_GetData(sql_handle,13, &data, NULL); item->attribute = atoi(data);
  54. Sql_GetData(sql_handle,14, &data, NULL); item->identify = atoi(data);
  55. for (j = 0; j < MAX_SLOTS; j++)
  56. {
  57. Sql_GetData(sql_handle, 15 + j, &data, NULL);
  58. item->card[j] = atoi(data);
  59. }
  60. }
  61. md->full = ( Sql_NumRows(sql_handle) > MAIL_MAX_INBOX );
  62. md->amount = i;
  63. md->changed = false;
  64. Sql_FreeResult(sql_handle);
  65. md->unchecked = 0;
  66. md->unread = 0;
  67. for (i = 0; i < md->amount; i++)
  68. {
  69. msg = &md->msg[i];
  70. if( msg->status == MAIL_NEW )
  71. {
  72. if ( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `status` = '%d' WHERE `id` = '%d'", mail_db, MAIL_UNREAD, msg->id) )
  73. Sql_ShowDebug(sql_handle);
  74. msg->status = MAIL_UNREAD;
  75. md->unchecked++;
  76. }
  77. else if ( msg->status == MAIL_UNREAD )
  78. md->unread++;
  79. }
  80. ShowInfo("mail load complete from DB - id: %d (total: %d)\n", char_id, md->amount);
  81. return 1;
  82. }
  83. /// Stores a single message in the database.
  84. /// Returns the message's ID if successful (or 0 if it fails).
  85. int mail_savemessage(struct mail_message* msg)
  86. {
  87. StringBuf buf;
  88. SqlStmt* stmt;
  89. int j;
  90. // build message save query
  91. StringBuf_Init(&buf);
  92. StringBuf_Printf(&buf, "INSERT INTO `%s` (`send_name`, `send_id`, `dest_name`, `dest_id`, `title`, `message`, `time`, `status`, `zeny`, `amount`, `nameid`, `refine`, `attribute`, `identify`", mail_db);
  93. for (j = 0; j < MAX_SLOTS; j++)
  94. StringBuf_Printf(&buf, ", `card%d`", j);
  95. StringBuf_Printf(&buf, ") VALUES (?, '%d', ?, '%d', ?, ?, '%lu', '%d', '%d', '%d', '%d', '%d', '%d', '%d'",
  96. msg->send_id, msg->dest_id, (unsigned long)msg->timestamp, msg->status, msg->zeny, msg->item.amount, msg->item.nameid, msg->item.refine, msg->item.attribute, msg->item.identify);
  97. for (j = 0; j < MAX_SLOTS; j++)
  98. StringBuf_Printf(&buf, ", '%d'", msg->item.card[j]);
  99. StringBuf_AppendStr(&buf, ")");
  100. // prepare and execute query
  101. stmt = SqlStmt_Malloc(sql_handle);
  102. if( SQL_SUCCESS != SqlStmt_PrepareStr(stmt, StringBuf_Value(&buf))
  103. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_STRING, msg->send_name, strnlen(msg->send_name, NAME_LENGTH))
  104. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_STRING, msg->dest_name, strnlen(msg->dest_name, NAME_LENGTH))
  105. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 2, SQLDT_STRING, msg->title, strnlen(msg->title, MAIL_TITLE_LENGTH))
  106. || SQL_SUCCESS != SqlStmt_BindParam(stmt, 3, SQLDT_STRING, msg->body, strnlen(msg->body, MAIL_BODY_LENGTH))
  107. || SQL_SUCCESS != SqlStmt_Execute(stmt) )
  108. {
  109. SqlStmt_ShowDebug(stmt);
  110. msg->id = 0;
  111. } else
  112. msg->id = (int)SqlStmt_LastInsertId(stmt);
  113. SqlStmt_Free(stmt);
  114. StringBuf_Destroy(&buf);
  115. return msg->id;
  116. }
  117. /// Retrieves a single message from the database.
  118. /// Returns true if the operation succeeds (or false if it fails).
  119. static bool mail_loadmessage(int mail_id, struct mail_message* msg)
  120. {
  121. int j;
  122. StringBuf buf;
  123. StringBuf_Init(&buf);
  124. StringBuf_AppendStr(&buf, "SELECT `id`,`send_name`,`send_id`,`dest_name`,`dest_id`,`title`,`message`,`time`,`status`,"
  125. "`zeny`,`amount`,`nameid`,`refine`,`attribute`,`identify`");
  126. for( j = 0; j < MAX_SLOTS; j++ )
  127. StringBuf_Printf(&buf, ",`card%d`", j);
  128. StringBuf_Printf(&buf, " FROM `%s` WHERE `id` = '%d'", mail_db, mail_id);
  129. if( SQL_ERROR == Sql_Query(sql_handle, StringBuf_Value(&buf))
  130. || SQL_SUCCESS != Sql_NextRow(sql_handle) )
  131. {
  132. Sql_ShowDebug(sql_handle);
  133. Sql_FreeResult(sql_handle);
  134. StringBuf_Destroy(&buf);
  135. return false;
  136. }
  137. else
  138. {
  139. char* data;
  140. Sql_GetData(sql_handle, 0, &data, NULL); msg->id = atoi(data);
  141. Sql_GetData(sql_handle, 1, &data, NULL); safestrncpy(msg->send_name, data, NAME_LENGTH);
  142. Sql_GetData(sql_handle, 2, &data, NULL); msg->send_id = atoi(data);
  143. Sql_GetData(sql_handle, 3, &data, NULL); safestrncpy(msg->dest_name, data, NAME_LENGTH);
  144. Sql_GetData(sql_handle, 4, &data, NULL); msg->dest_id = atoi(data);
  145. Sql_GetData(sql_handle, 5, &data, NULL); safestrncpy(msg->title, data, MAIL_TITLE_LENGTH);
  146. Sql_GetData(sql_handle, 6, &data, NULL); safestrncpy(msg->body, data, MAIL_BODY_LENGTH);
  147. Sql_GetData(sql_handle, 7, &data, NULL); msg->timestamp = atoi(data);
  148. Sql_GetData(sql_handle, 8, &data, NULL); msg->status = (mail_status)atoi(data);
  149. Sql_GetData(sql_handle, 9, &data, NULL); msg->zeny = atoi(data);
  150. Sql_GetData(sql_handle,10, &data, NULL); msg->item.amount = (short)atoi(data);
  151. Sql_GetData(sql_handle,11, &data, NULL); msg->item.nameid = atoi(data);
  152. Sql_GetData(sql_handle,12, &data, NULL); msg->item.refine = atoi(data);
  153. Sql_GetData(sql_handle,13, &data, NULL); msg->item.attribute = atoi(data);
  154. Sql_GetData(sql_handle,14, &data, NULL); msg->item.identify = atoi(data);
  155. for( j = 0; j < MAX_SLOTS; j++ )
  156. {
  157. Sql_GetData(sql_handle,15 + j, &data, NULL);
  158. msg->item.card[j] = atoi(data);
  159. }
  160. }
  161. StringBuf_Destroy(&buf);
  162. Sql_FreeResult(sql_handle);
  163. return true;
  164. }
  165. /*==========================================
  166. * Client Inbox Request
  167. *------------------------------------------*/
  168. static void mapif_Mail_sendinbox(int fd, int char_id, unsigned char flag)
  169. {
  170. struct mail_data md;
  171. mail_fromsql(char_id, &md);
  172. //FIXME: dumping the whole structure like this is unsafe [ultramage]
  173. WFIFOHEAD(fd, sizeof(md) + 9);
  174. WFIFOW(fd,0) = 0x3848;
  175. WFIFOW(fd,2) = sizeof(md) + 9;
  176. WFIFOL(fd,4) = char_id;
  177. WFIFOB(fd,8) = flag;
  178. memcpy(WFIFOP(fd,9),&md,sizeof(md));
  179. WFIFOSET(fd,WFIFOW(fd,2));
  180. }
  181. static void mapif_parse_Mail_requestinbox(int fd)
  182. {
  183. mapif_Mail_sendinbox(fd, RFIFOL(fd,2), RFIFOB(fd,6));
  184. }
  185. /*==========================================
  186. * Mark mail as 'Read'
  187. *------------------------------------------*/
  188. static void mapif_parse_Mail_read(int fd)
  189. {
  190. int mail_id = RFIFOL(fd,2);
  191. if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `status` = '%d' WHERE `id` = '%d'", mail_db, MAIL_READ, mail_id) )
  192. Sql_ShowDebug(sql_handle);
  193. }
  194. /*==========================================
  195. * Client Attachment Request
  196. *------------------------------------------*/
  197. static bool mail_DeleteAttach(int mail_id)
  198. {
  199. StringBuf buf;
  200. int i;
  201. StringBuf_Init(&buf);
  202. StringBuf_Printf(&buf, "UPDATE `%s` SET `zeny` = '0', `nameid` = '0', `amount` = '0', `refine` = '0', `attribute` = '0', `identify` = '0'", mail_db);
  203. for (i = 0; i < MAX_SLOTS; i++)
  204. StringBuf_Printf(&buf, ", `card%d` = '0'", i);
  205. StringBuf_Printf(&buf, " WHERE `id` = '%d'", mail_id);
  206. if( SQL_ERROR == Sql_Query(sql_handle, StringBuf_Value(&buf)) )
  207. {
  208. Sql_ShowDebug(sql_handle);
  209. StringBuf_Destroy(&buf);
  210. return false;
  211. }
  212. StringBuf_Destroy(&buf);
  213. return true;
  214. }
  215. static void mapif_Mail_getattach(int fd, int char_id, int mail_id)
  216. {
  217. struct mail_message msg;
  218. if( !mail_loadmessage(mail_id, &msg) )
  219. return;
  220. if( msg.dest_id != char_id )
  221. return;
  222. if( msg.status != MAIL_READ )
  223. return;
  224. if( (msg.item.nameid < 1 || msg.item.amount < 1) && msg.zeny < 1 )
  225. return; // No Attachment
  226. if( !mail_DeleteAttach(mail_id) )
  227. return;
  228. WFIFOHEAD(fd, sizeof(struct item) + 12);
  229. WFIFOW(fd,0) = 0x384a;
  230. WFIFOW(fd,2) = sizeof(struct item) + 12;
  231. WFIFOL(fd,4) = char_id;
  232. WFIFOL(fd,8) = (msg.zeny > 0)?msg.zeny:0;
  233. memcpy(WFIFOP(fd,12), &msg.item, sizeof(struct item));
  234. WFIFOSET(fd,WFIFOW(fd,2));
  235. }
  236. static void mapif_parse_Mail_getattach(int fd)
  237. {
  238. mapif_Mail_getattach(fd, RFIFOL(fd,2), RFIFOL(fd,6));
  239. }
  240. /*==========================================
  241. * Delete Mail
  242. *------------------------------------------*/
  243. static void mapif_Mail_delete(int fd, int char_id, int mail_id)
  244. {
  245. bool failed = false;
  246. if ( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `id` = '%d'", mail_db, mail_id) )
  247. {
  248. Sql_ShowDebug(sql_handle);
  249. failed = true;
  250. }
  251. WFIFOHEAD(fd,11);
  252. WFIFOW(fd,0) = 0x384b;
  253. WFIFOL(fd,2) = char_id;
  254. WFIFOL(fd,6) = mail_id;
  255. WFIFOB(fd,10) = failed;
  256. WFIFOSET(fd,11);
  257. }
  258. static void mapif_parse_Mail_delete(int fd)
  259. {
  260. mapif_Mail_delete(fd, RFIFOL(fd,2), RFIFOL(fd,6));
  261. }
  262. /*==========================================
  263. * Report New Mail to Map Server
  264. *------------------------------------------*/
  265. void mapif_Mail_new(struct mail_message *msg)
  266. {
  267. unsigned char buf[74];
  268. if( !msg || !msg->id )
  269. return;
  270. WBUFW(buf,0) = 0x3849;
  271. WBUFL(buf,2) = msg->dest_id;
  272. WBUFL(buf,6) = msg->id;
  273. memcpy(WBUFP(buf,10), msg->send_name, NAME_LENGTH);
  274. memcpy(WBUFP(buf,34), msg->title, MAIL_TITLE_LENGTH);
  275. mapif_sendall(buf, 74);
  276. }
  277. /*==========================================
  278. * Return Mail
  279. *------------------------------------------*/
  280. static void mapif_Mail_return(int fd, int char_id, int mail_id)
  281. {
  282. struct mail_message msg;
  283. int new_mail = 0;
  284. if( mail_loadmessage(mail_id, &msg) )
  285. {
  286. if( msg.dest_id != char_id)
  287. return;
  288. else if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `id` = '%d'", mail_db, mail_id) )
  289. Sql_ShowDebug(sql_handle);
  290. else
  291. {
  292. char temp_[MAIL_TITLE_LENGTH];
  293. // swap sender and receiver
  294. swap(msg.send_id, msg.dest_id);
  295. safestrncpy(temp_, msg.send_name, NAME_LENGTH);
  296. safestrncpy(msg.send_name, msg.dest_name, NAME_LENGTH);
  297. safestrncpy(msg.dest_name, temp_, NAME_LENGTH);
  298. // set reply message title
  299. snprintf(temp_, MAIL_TITLE_LENGTH, "RE:%s", msg.title);
  300. safestrncpy(msg.title, temp_, MAIL_TITLE_LENGTH);
  301. msg.status = MAIL_NEW;
  302. msg.timestamp = time(NULL);
  303. new_mail = mail_savemessage(&msg);
  304. mapif_Mail_new(&msg);
  305. }
  306. }
  307. WFIFOHEAD(fd,11);
  308. WFIFOW(fd,0) = 0x384c;
  309. WFIFOL(fd,2) = char_id;
  310. WFIFOL(fd,6) = mail_id;
  311. WFIFOB(fd,10) = (new_mail == 0);
  312. WFIFOSET(fd,11);
  313. }
  314. static void mapif_parse_Mail_return(int fd)
  315. {
  316. mapif_Mail_return(fd, RFIFOL(fd,2), RFIFOL(fd,6));
  317. }
  318. /*==========================================
  319. * Send Mail
  320. *------------------------------------------*/
  321. static void mapif_Mail_send(int fd, struct mail_message* msg)
  322. {
  323. int len = sizeof(struct mail_message) + 4;
  324. WFIFOHEAD(fd,len);
  325. WFIFOW(fd,0) = 0x384d;
  326. WFIFOW(fd,2) = len;
  327. memcpy(WFIFOP(fd,4), msg, sizeof(struct mail_message));
  328. WFIFOSET(fd,len);
  329. }
  330. static void mapif_parse_Mail_send(int fd)
  331. {
  332. struct mail_message msg;
  333. char esc_name[NAME_LENGTH*2+1];
  334. int account_id = 0;
  335. if(RFIFOW(fd,2) != 8 + sizeof(struct mail_message))
  336. return;
  337. account_id = RFIFOL(fd,4);
  338. memcpy(&msg, RFIFOP(fd,8), sizeof(struct mail_message));
  339. // Try to find the Dest Char by Name
  340. Sql_EscapeStringLen(sql_handle, esc_name, msg.dest_name, strnlen(msg.dest_name, NAME_LENGTH));
  341. if ( SQL_ERROR == Sql_Query(sql_handle, "SELECT `account_id`, `char_id` FROM `%s` WHERE `name` = '%s'", char_db, esc_name) )
  342. Sql_ShowDebug(sql_handle);
  343. else
  344. if ( SQL_SUCCESS == Sql_NextRow(sql_handle) )
  345. {
  346. char *data;
  347. Sql_GetData(sql_handle, 0, &data, NULL);
  348. if (atoi(data) != account_id)
  349. { // Cannot send mail to char in the same account
  350. Sql_GetData(sql_handle, 1, &data, NULL);
  351. msg.dest_id = atoi(data);
  352. }
  353. }
  354. Sql_FreeResult(sql_handle);
  355. msg.status = MAIL_NEW;
  356. if( msg.dest_id > 0 )
  357. msg.id = mail_savemessage(&msg);
  358. mapif_Mail_send(fd, &msg);
  359. }
  360. void 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)
  361. {
  362. struct mail_message msg;
  363. memset(&msg, 0, sizeof(struct mail_message));
  364. msg.send_id = send_id;
  365. safestrncpy(msg.send_name, send_name, NAME_LENGTH);
  366. msg.dest_id = dest_id;
  367. safestrncpy(msg.dest_name, dest_name, NAME_LENGTH);
  368. safestrncpy(msg.title, title, MAIL_TITLE_LENGTH);
  369. safestrncpy(msg.body, body, MAIL_BODY_LENGTH);
  370. msg.zeny = zeny;
  371. if( item != NULL )
  372. memcpy(&msg.item, item, sizeof(struct item));
  373. msg.timestamp = time(NULL);
  374. mail_savemessage(&msg);
  375. mapif_Mail_new(&msg);
  376. }
  377. /*==========================================
  378. * Packets From Map Server
  379. *------------------------------------------*/
  380. int inter_mail_parse_frommap(int fd)
  381. {
  382. switch(RFIFOW(fd,0))
  383. {
  384. case 0x3048: mapif_parse_Mail_requestinbox(fd); break;
  385. case 0x3049: mapif_parse_Mail_read(fd); break;
  386. case 0x304a: mapif_parse_Mail_getattach(fd); break;
  387. case 0x304b: mapif_parse_Mail_delete(fd); break;
  388. case 0x304c: mapif_parse_Mail_return(fd); break;
  389. case 0x304d: mapif_parse_Mail_send(fd); break;
  390. default:
  391. return 0;
  392. }
  393. return 1;
  394. }
  395. int inter_mail_sql_init(void)
  396. {
  397. return 1;
  398. }
  399. void inter_mail_sql_final(void)
  400. {
  401. return;
  402. }