ZNC Modules for SQLite and MySQL
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

936 lines
37 KiB

13 years ago
  1. //
  2. // logmysql.cpp
  3. // LogSQL
  4. //
  5. // Created by Mr. Gecko on 2/10/12.
  6. // Copyright (c) 2012 Mr. Gecko's Media (James Coleman). http://mrgeckosmedia.com/
  7. //
  8. // Permission to use, copy, modify, and/or distribute this software for any purpose
  9. // with or without fee is hereby granted, provided that the above copyright notice
  10. // and this permission notice appear in all copies.
  11. //
  12. // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
  13. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
  14. // FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
  15. // OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
  16. // DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
  17. // ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  18. //
  19. #include <znc/Chan.h>
  20. #include <znc/User.h>
  21. #include <znc/IRCNetwork.h>
  22. #include <znc/Modules.h>
  23. #include <time.h>
  24. #import <mysql.h>
  25. class CLogMySQL : public CModule {
  26. public:
  27. MODCONSTRUCTOR(CLogMySQL) {}
  28. virtual bool OnLoad(const CString& sArgs, CString& sMessage) {
  29. connected = true;
  30. databaseConnected = false;
  31. MySQLConnect();
  32. return true;
  33. }
  34. virtual ~CLogMySQL() {
  35. if (databaseConnected) {
  36. databaseConnected = false;
  37. mysql_close(database);
  38. database = NULL;
  39. }
  40. }
  41. void MySQLConnect() {
  42. CString host = GetNV("host");
  43. CString port = GetNV("port");
  44. if (port.empty()) {
  45. port = "3306";
  46. SetNV("port",port);
  47. }
  48. CString username = GetNV("username");
  49. CString password = GetNV("password");
  50. CString databaseName = GetNV("databaseName");
  51. if (!host.empty() && !username.empty() && !databaseName.empty()) {
  52. if (databaseConnected) {
  53. databaseConnected = false;
  54. mysql_close(database);
  55. database = NULL;
  56. }
  57. database = mysql_init(NULL);
  58. if (database!=NULL) {
  59. void *theRet = mysql_real_connect(database, host.c_str(), username.c_str(), password.c_str(), databaseName.c_str(), (unsigned int)strtoul(port.c_str(), NULL, 10), MYSQL_UNIX_ADDR, CLIENT_COMPRESS);
  60. databaseConnected = (theRet==database);
  61. if (databaseConnected) {
  62. cout << "LogMySQL: Database connected.\n";
  63. MYSQL_RES *settings = mysql_list_tables(database, "settings");
  64. MYSQL_RES *messages = mysql_list_tables(database, "messages");
  65. if (mysql_num_rows(settings)==0 || mysql_num_rows(messages)==0) {
  66. cout << "Creating tables\n";
  67. MYSQL_STMT *statement = mysql_stmt_init(database);
  68. int status = mysql_stmt_prepare(statement, "DROP TABLE IF EXISTS `settings`", 31);
  69. if (status==0) {
  70. mysql_stmt_execute(statement);
  71. mysql_stmt_close(statement);
  72. }
  73. statement = mysql_stmt_init(database);
  74. status = mysql_stmt_prepare(statement, "CREATE TABLE `settings` (`name` text,`value` text)", 50);
  75. if (status==0) {
  76. mysql_stmt_execute(statement);
  77. mysql_stmt_close(statement);
  78. }
  79. statement = mysql_stmt_init(database);
  80. status = mysql_stmt_prepare(statement, "DROP TABLE IF EXISTS `messages`", 31);
  81. if (status==0) {
  82. mysql_stmt_execute(statement);
  83. mysql_stmt_close(statement);
  84. }
  85. statement = mysql_stmt_init(database);
  86. status = mysql_stmt_prepare(statement, "CREATE TABLE `messages` (`rowid` int UNSIGNED AUTO_INCREMENT,`target` text,`nick` text,`type` text,`message` longblob,`time` decimal(20,5) UNSIGNED,PRIMARY KEY (`rowid`))", 170);
  87. if (status==0) {
  88. mysql_stmt_execute(statement);
  89. mysql_stmt_close(statement);
  90. }
  91. SetSetting("replayAll","0");
  92. SetSetting("logLimit","1");
  93. SetSetting("logLevel","1");
  94. }
  95. if (settings!=NULL)
  96. mysql_free_result(settings);
  97. if (messages!=NULL)
  98. mysql_free_result(messages);
  99. replayAll = atoi(GetSetting("replayAll").c_str());
  100. logLimit = strtoul(GetSetting("logLimit").c_str(), NULL, 10);
  101. logLevel = atoi(GetSetting("logLevel").c_str());
  102. } else {
  103. cout << "LogMySQL: Database unable to connect.\n";
  104. }
  105. }
  106. }
  107. }
  108. CString GetUNIXTime() {
  109. struct timeval time;
  110. gettimeofday(&time, NULL);
  111. double microtime = (double)(time.tv_sec + (time.tv_usec/1000000.00));
  112. char timeStr[25];
  113. snprintf(timeStr, sizeof(timeStr), "%f", microtime);
  114. return timeStr;
  115. }
  116. void AddMessage(const CString& target, const CString& nick, const CString& type, const CString& message) {
  117. if (!databaseConnected)
  118. return;
  119. MYSQL_STMT *statement = mysql_stmt_init(database);
  120. int status = mysql_stmt_prepare(statement, "INSERT INTO `messages` (`target`, `nick`, `type`, `message`, `time`) VALUES (?,?,?,?,?)", 87);
  121. if (status!=0)
  122. return;
  123. MYSQL_BIND bind[5];
  124. memset(bind, 0, sizeof(bind));
  125. bind[0].buffer_type = MYSQL_TYPE_STRING;
  126. bind[0].buffer = (void*)target.c_str();
  127. bind[0].buffer_length = target.length();
  128. bind[0].is_null = false;
  129. bind[1].buffer_type = MYSQL_TYPE_STRING;
  130. bind[1].buffer = (void*)nick.c_str();
  131. bind[1].buffer_length = nick.length();
  132. bind[1].is_null = false;
  133. bind[2].buffer_type = MYSQL_TYPE_STRING;
  134. bind[2].buffer = (void*)type.c_str();
  135. bind[2].buffer_length = type.length();
  136. bind[2].is_null = false;
  137. bind[3].buffer_type = MYSQL_TYPE_STRING;
  138. bind[3].buffer = (void*)message.c_str();
  139. bind[3].buffer_length = message.length();
  140. bind[3].is_null = false;
  141. CString time = GetUNIXTime();
  142. bind[4].buffer_type = MYSQL_TYPE_STRING;
  143. bind[4].buffer = (void*)time.c_str();
  144. bind[4].buffer_length = time.length();
  145. bind[4].is_null = false;
  146. status = mysql_stmt_bind_param(statement, bind);
  147. if (status!=0) {
  148. mysql_stmt_close(statement);
  149. return;
  150. }
  151. mysql_stmt_execute(statement);
  152. mysql_stmt_close(statement);
  153. if (logLimit>=2) {
  154. statement = mysql_stmt_init(database);
  155. status = mysql_stmt_prepare(statement, "SELECT COUNT(*) FROM `messages`", 31);
  156. if (status!=0)
  157. return;
  158. status = mysql_stmt_execute(statement);
  159. if (status!=0) {
  160. mysql_stmt_close(statement);
  161. return;
  162. }
  163. MYSQL_RES *result = mysql_stmt_result_metadata(statement);
  164. unsigned int dataCount = mysql_num_fields(result);
  165. if (dataCount!=1) {
  166. mysql_free_result(result);
  167. mysql_stmt_close(statement);
  168. cout << "LogMySQL: We are only suppose to receive 1 field. We received the count of " << dataCount << " fields.\n";
  169. return;
  170. }
  171. MYSQL_FIELD *fields = mysql_fetch_fields(result);
  172. unsigned long length;
  173. char countData[fields[0].length];
  174. MYSQL_BIND results[1];
  175. memset(results, 0, sizeof(results));
  176. results[0].buffer_type = MYSQL_TYPE_STRING;
  177. results[0].buffer = (void *)countData;
  178. results[0].buffer_length = fields[0].length;
  179. results[0].length = &length;
  180. status = mysql_stmt_bind_result(statement, results);
  181. if (status!=0) {
  182. mysql_free_result(result);
  183. mysql_stmt_close(statement);
  184. return;
  185. }
  186. status = mysql_stmt_fetch(statement);
  187. if (status!=0) {
  188. mysql_free_result(result);
  189. mysql_stmt_close(statement);
  190. return;
  191. }
  192. CString countString = CString(countData, length);
  193. unsigned long count = strtoul(countString.c_str(), NULL, 10);
  194. if (count<=logLimit)
  195. count = 0;
  196. else
  197. count = count-logLimit;
  198. mysql_free_result(result);
  199. mysql_stmt_close(statement);
  200. if (count!=0) {
  201. statement = mysql_stmt_init(database);
  202. char *queryStr = (char *)malloc(73);
  203. snprintf(queryStr, 73, "SELECT `rowid` FROM `messages` ORDER BY `time` LIMIT %lu", count);
  204. status = mysql_stmt_prepare(statement, queryStr, strlen(queryStr));
  205. free(queryStr);
  206. if (status!=0)
  207. return;
  208. status = mysql_stmt_execute(statement);
  209. if (status!=0) {
  210. mysql_stmt_close(statement);
  211. return;
  212. }
  213. result = mysql_stmt_result_metadata(statement);
  214. dataCount = mysql_num_fields(result);
  215. if (dataCount!=1) {
  216. mysql_free_result(result);
  217. mysql_stmt_close(statement);
  218. cout << "LogMySQL: We are only suppose to receive 1 field. We received the count of " << dataCount << " fields.\n";
  219. return;
  220. }
  221. fields = mysql_fetch_fields(result);
  222. char rowidData[fields[0].length];
  223. memset(results, 0, sizeof(results));
  224. results[0].buffer_type = MYSQL_TYPE_STRING;
  225. results[0].buffer = (void *)rowidData;
  226. results[0].buffer_length = fields[0].length;
  227. results[0].length = &length;
  228. status = mysql_stmt_bind_result(statement, results);
  229. if (status!=0) {
  230. mysql_free_result(result);
  231. mysql_stmt_close(statement);
  232. return;
  233. }
  234. while (true) {
  235. status = mysql_stmt_fetch(statement);
  236. if (status!=0)
  237. break;
  238. CString rowid = CString(rowidData, length);
  239. MYSQL_STMT *statement2 = mysql_stmt_init(database);
  240. status = mysql_stmt_prepare(statement2, "DELETE FROM `messages` WHERE `rowid`=?", 38);
  241. if (status!=0)
  242. continue;
  243. MYSQL_BIND bind2[1];
  244. memset(bind, 0, sizeof(bind));
  245. bind2[0].buffer_type = MYSQL_TYPE_STRING;
  246. bind2[0].buffer = (void*)rowid.c_str();
  247. bind2[0].buffer_length = rowid.length();
  248. bind2[0].is_null = false;
  249. status = mysql_stmt_bind_param(statement2, bind2);
  250. if (status!=0) {
  251. mysql_stmt_close(statement2);
  252. continue;
  253. }
  254. mysql_stmt_execute(statement2);
  255. mysql_stmt_close(statement2);
  256. }
  257. mysql_free_result(result);
  258. mysql_stmt_close(statement);
  259. }
  260. }
  261. }
  262. void SetSetting(const CString& name, const CString& value) {
  263. if (!databaseConnected)
  264. return;
  265. bool exists = false;
  266. MYSQL_STMT *statement = mysql_stmt_init(database);
  267. int status = mysql_stmt_prepare(statement, "SELECT `value` FROM `settings` WHERE `name`=?", 45);
  268. if (status==0) {
  269. MYSQL_BIND bind[1];
  270. memset(bind, 0, sizeof(bind));
  271. bind[0].buffer_type = MYSQL_TYPE_STRING;
  272. bind[0].buffer = (void*)name.c_str();
  273. bind[0].buffer_length = name.length();
  274. bind[0].is_null = false;
  275. status = mysql_stmt_bind_param(statement, bind);
  276. if (status==0) {
  277. mysql_stmt_execute(statement);
  278. status = mysql_stmt_fetch(statement);
  279. if (status==0) {
  280. exists = true;
  281. }
  282. }
  283. mysql_stmt_close(statement);
  284. }
  285. if (exists) {
  286. statement = mysql_stmt_init(database);
  287. status = mysql_stmt_prepare(statement, "UPDATE `settings` SET `value`=? WHERE `name`=?", 46);
  288. if (status!=0)
  289. return;
  290. MYSQL_BIND bind[2];
  291. memset(bind, 0, sizeof(bind));
  292. bind[0].buffer_type = MYSQL_TYPE_STRING;
  293. bind[0].buffer = (void*)value.c_str();
  294. bind[0].buffer_length = value.length();
  295. bind[0].is_null = false;
  296. bind[1].buffer_type = MYSQL_TYPE_STRING;
  297. bind[1].buffer = (void*)name.c_str();
  298. bind[1].buffer_length = name.length();
  299. bind[1].is_null = false;
  300. status = mysql_stmt_bind_param(statement, bind);
  301. if (status!=0) {
  302. mysql_stmt_close(statement);
  303. return;
  304. }
  305. mysql_stmt_execute(statement);
  306. mysql_stmt_close(statement);
  307. } else {
  308. statement = mysql_stmt_init(database);
  309. status = mysql_stmt_prepare(statement, "INSERT INTO `settings` (`name`,`value`) VALUES (?,?)", 52);
  310. if (status!=0)
  311. return;
  312. MYSQL_BIND bind[2];
  313. memset(bind, 0, sizeof(bind));
  314. bind[0].buffer_type = MYSQL_TYPE_STRING;
  315. bind[0].buffer = (void*)name.c_str();
  316. bind[0].buffer_length = name.length();
  317. bind[0].is_null = false;
  318. bind[1].buffer_type = MYSQL_TYPE_STRING;
  319. bind[1].buffer = (void*)value.c_str();
  320. bind[1].buffer_length = value.length();
  321. bind[1].is_null = false;
  322. status = mysql_stmt_bind_param(statement, bind);
  323. if (status!=0) {
  324. mysql_stmt_close(statement);
  325. return;
  326. }
  327. mysql_stmt_execute(statement);
  328. mysql_stmt_close(statement);
  329. }
  330. }
  331. CString GetSetting(const CString& name) {
  332. CString stringValue;
  333. if (!databaseConnected)
  334. return stringValue;
  335. MYSQL_STMT *statement = mysql_stmt_init(database);
  336. int status = mysql_stmt_prepare(statement, "SELECT `value` FROM `settings` WHERE `name`=?", 45);
  337. if (status!=0)
  338. return stringValue;
  339. MYSQL_BIND bind[1];
  340. memset(bind, 0, sizeof(bind));
  341. bind[0].buffer_type = MYSQL_TYPE_STRING;
  342. bind[0].buffer = (void*)name.c_str();
  343. bind[0].buffer_length = name.length();
  344. bind[0].is_null = false;
  345. status = mysql_stmt_bind_param(statement, bind);
  346. if (status!=0) {
  347. mysql_stmt_close(statement);
  348. return stringValue;
  349. }
  350. status = mysql_stmt_execute(statement);
  351. if (status!=0) {
  352. mysql_stmt_close(statement);
  353. return stringValue;
  354. }
  355. MYSQL_RES *result = mysql_stmt_result_metadata(statement);
  356. unsigned int dataCount = mysql_num_fields(result);
  357. if (dataCount!=1) {
  358. mysql_free_result(result);
  359. mysql_stmt_close(statement);
  360. cout << "LogMySQL: Settings are only suppose to return 1 as the field count. We received the count of " << dataCount << " fields.\n";
  361. return stringValue;
  362. }
  363. MYSQL_FIELD *fields = mysql_fetch_fields(result);
  364. unsigned long length;
  365. char valueData[fields[0].length];
  366. MYSQL_BIND results[1];
  367. memset(results, 0, sizeof(results));
  368. results[0].buffer_type = MYSQL_TYPE_STRING;
  369. results[0].buffer = (void *)valueData;
  370. results[0].buffer_length = fields[0].length;
  371. results[0].length = &length;
  372. status = mysql_stmt_bind_result(statement, results);
  373. if (status!=0) {
  374. mysql_free_result(result);
  375. mysql_stmt_close(statement);
  376. return stringValue;
  377. }
  378. while (true) {
  379. status = mysql_stmt_fetch(statement);
  380. if (status!=0)
  381. break;
  382. stringValue = CString(valueData, length);
  383. break;
  384. }
  385. mysql_free_result(result);
  386. mysql_stmt_close(statement);
  387. return stringValue;
  388. }
  389. //Server stuff
  390. virtual void OnIRCDisconnected() {
  391. if (connected) {
  392. connected = false;
  393. if (logLevel>=3) {
  394. AddMessage("","","DISCONNECT","");
  395. }
  396. }
  397. }
  398. virtual void OnIRCConnected() {
  399. if (!connected) {
  400. connected = true;
  401. if (logLevel>=3) {
  402. AddMessage("","","CONNECT","");
  403. }
  404. }
  405. }
  406. //User stuff
  407. virtual EModRet OnUserAction(CString& sTarget, CString& sMessage) {
  408. if (logLevel>=4) {
  409. AddMessage(sTarget,m_pNetwork->GetIRCNick().GetNickMask(),"ACTION",sMessage);
  410. }
  411. return CONTINUE;
  412. }
  413. virtual EModRet OnUserMsg(CString& sTarget, CString& sMessage) {
  414. if (logLevel>=4) {
  415. AddMessage(sTarget,m_pNetwork->GetIRCNick().GetNickMask(),"PRIVMSG",sMessage);
  416. }
  417. return CONTINUE;
  418. }
  419. virtual EModRet OnUserNotice(CString& sTarget, CString& sMessage) {
  420. if (logLevel>=4) {
  421. AddMessage(sTarget,m_pNetwork->GetIRCNick().GetNickMask(),"NOTICE",sMessage);
  422. }
  423. return CONTINUE;
  424. }
  425. virtual EModRet OnUserJoin(CString& sChannel, CString& sKey) {
  426. if (logLevel>=4) {
  427. AddMessage(sChannel,m_pNetwork->GetIRCNick().GetNickMask(),"JOIN","");
  428. }
  429. return CONTINUE;
  430. }
  431. virtual EModRet OnUserPart(CString& sChannel, CString& sMessage) {
  432. if (logLevel>=4) {
  433. AddMessage(sChannel,m_pNetwork->GetIRCNick().GetNickMask(),"PART",sMessage);
  434. }
  435. return CONTINUE;
  436. }
  437. virtual EModRet OnUserTopic(CString& sChannel, CString& sTopic) {
  438. if (logLevel>=4) {
  439. AddMessage(sChannel,m_pNetwork->GetIRCNick().GetNickMask(),"TOPIC",sTopic);
  440. }
  441. return CONTINUE;
  442. }
  443. //Other stuff
  444. virtual void OnRawMode(const CNick& OpNick, CChan& Channel, const CString& sModes, const CString& sArgs) {
  445. if (logLevel>=3) {
  446. AddMessage(Channel.GetName(),OpNick.GetNickMask(),"MODE",sModes+" "+sArgs);
  447. }
  448. }
  449. virtual void OnKick(const CNick& OpNick, const CString& sKickedNick, CChan& Channel, const CString& sMessage) {
  450. if (logLevel>=2) {
  451. AddMessage(Channel.GetName(),OpNick.GetNickMask(),"KICK",sKickedNick+" "+sMessage);
  452. }
  453. }
  454. virtual void OnQuit(const CNick& Nick, const CString& sMessage, const vector<CChan*>& vChans) {
  455. if (logLevel>=2) {
  456. vector<CChan*>::const_iterator it;
  457. for (it=vChans.begin(); it!=vChans.end(); it++) {
  458. CChan& channel = **it;
  459. AddMessage(channel.GetName(),Nick.GetNickMask(),"QUIT",sMessage);
  460. }
  461. }
  462. }
  463. virtual void OnJoin(const CNick& Nick, CChan& Channel) {
  464. if (logLevel>=2) {
  465. AddMessage(Channel.GetName(),Nick.GetNickMask(),"JOIN","");
  466. }
  467. }
  468. virtual void OnPart(const CNick& Nick, CChan& Channel, const CString& sMessage) {
  469. if (logLevel>=2) {
  470. AddMessage(Channel.GetName(),Nick.GetNickMask(),"JOIN",sMessage);
  471. }
  472. }
  473. virtual void OnNick(const CNick& OldNick, const CString& sNewNick, const vector<CChan*>& vChans) {
  474. if (logLevel>=2) {
  475. vector<CChan*>::const_iterator it;
  476. for (it=vChans.begin(); it!=vChans.end(); it++) {
  477. CChan& channel = **it;
  478. AddMessage(channel.GetName(),OldNick.GetNickMask(),"NICK",sNewNick);
  479. }
  480. }
  481. }
  482. virtual EModRet OnPrivAction(CNick& Nick, CString& sMessage) {
  483. if (logLevel>=0) {
  484. AddMessage(m_pNetwork->GetCurNick(),Nick.GetNickMask(),"ACTION",sMessage);
  485. }
  486. return CONTINUE;
  487. }
  488. virtual EModRet OnChanAction(CNick& Nick, CChan& Channel, CString& sMessage) {
  489. if (logLevel==0) {
  490. if (strcasestr(sMessage.c_str(),m_pNetwork->GetCurNick().c_str()))
  491. AddMessage(Channel.GetName(),Nick.GetNickMask(),"ACTION",sMessage);
  492. } else if (logLevel>=1) {
  493. AddMessage(Channel.GetName(),Nick.GetNickMask(),"ACTION",sMessage);
  494. }
  495. return CONTINUE;
  496. }
  497. virtual EModRet OnPrivMsg(CNick& Nick, CString& sMessage) {
  498. if (logLevel>=0) {
  499. AddMessage(m_pNetwork->GetCurNick(),Nick.GetNickMask(),"PRIVMSG",sMessage);
  500. }
  501. return CONTINUE;
  502. }
  503. virtual EModRet OnChanMsg(CNick& Nick, CChan& Channel, CString& sMessage) {
  504. if (logLevel==0) {
  505. if (strcasestr(sMessage.c_str(),m_pNetwork->GetCurNick().c_str()))
  506. AddMessage(Channel.GetName(),Nick.GetNickMask(),"PRIVMSG",sMessage);
  507. } else if (logLevel>=1) {
  508. AddMessage(Channel.GetName(),Nick.GetNickMask(),"PRIVMSG",sMessage);
  509. }
  510. return CONTINUE;
  511. }
  512. virtual EModRet OnPrivNotice(CNick& Nick, CString& sMessage) {
  513. if (logLevel>=2) {
  514. AddMessage(m_pNetwork->GetCurNick(),Nick.GetNickMask(),"NOTICE",sMessage);
  515. }
  516. return CONTINUE;
  517. }
  518. virtual EModRet OnChanNotice(CNick& Nick, CChan& Channel, CString& sMessage) {
  519. if (logLevel>=2) {
  520. AddMessage(Channel.GetName(),Nick.GetNickMask(),"NOTICE",sMessage);
  521. }
  522. return CONTINUE;
  523. }
  524. virtual EModRet OnTopic(CNick& Nick, CChan& Channel, CString& sTopic) {
  525. if (logLevel>=2) {
  526. AddMessage(Channel.GetName(),Nick.GetNickMask(),"TOPIC",sTopic);
  527. }
  528. return CONTINUE;
  529. }
  530. virtual EModRet OnRaw(CString& sLine) {
  531. if (logLevel>=3) {
  532. CString sCmd = sLine.Token(1);
  533. if ((sCmd.length() == 3) && (isdigit(sCmd[0])) && (isdigit(sCmd[1])) && (isdigit(sCmd[2]))) {
  534. unsigned int uRaw = sCmd.ToUInt();
  535. if (uRaw!=10 && uRaw!=305 && uRaw!=306 && uRaw!=324 && uRaw!=329 && uRaw<331 && uRaw>333 && uRaw!=352 && uRaw!=353 && uRaw!=366 && uRaw!=432 && uRaw!=433 && uRaw!=437 && uRaw!=451 && uRaw!=670) {
  536. AddMessage("",sLine.Token(0),sCmd,sLine.Token(2, true));
  537. }
  538. }
  539. }
  540. return CONTINUE;
  541. }
  542. //Client Connection
  543. virtual void OnClientLogin() {
  544. SetSetting("clientConnected",GetUNIXTime());
  545. Replay();
  546. }
  547. virtual void OnClientDisconnect() {
  548. if (!m_pNetwork->IsUserAttached()) {
  549. SetSetting("clientDisconnected",GetUNIXTime());
  550. }
  551. }
  552. virtual void OnModCommand(const CString& sCmdLine) {
  553. CString sCommand = sCmdLine.Token(0);
  554. CString sArgs = sCmdLine.Token(1, true);
  555. if (sCommand.Equals("HELP")) {
  556. CString host = GetNV("host");
  557. CString port = GetNV("port");
  558. if (port.empty()) {
  559. port = "3306";
  560. SetNV("port",port);
  561. }
  562. CString username = GetNV("username");
  563. CString password = GetNV("password");
  564. CString databaseName = GetNV("databaseName");
  565. PutModule("host - MySQL host.");
  566. PutModule("port - MySQL port (defualt is 3306).");
  567. PutModule("username - MySQL username.");
  568. PutModule("password - MySQL password.");
  569. PutModule("databaseName - MySQL database name.");
  570. PutModule("-----");
  571. PutModule("connect - Reconnect to the MySQL database.");
  572. PutModule("-----");
  573. PutModule("replay - Play back the messages received.");
  574. PutModule("replayAll - Set LogMySQL to replay all messages stored (default is off).");
  575. PutModule("logLimit - Set LogMySQL to limit the amount of items to store into the log (0 means to keep everything, 1 means clear after replay, anything else would limit to the count, default is 1).");
  576. PutModule("logLevel - Set LogMySQL log level (0 messages mentioning you and/or to you only. 1 include all messages sent in an channel. 2 include all actions, join/part, and notices sent in channel and to you. 3 include all server wide messages. 4 include messages sent by you. Default is 1).");
  577. PutModule("-----");
  578. PutModule("Layout is as below.");
  579. PutModule("Command [arguments]");
  580. PutModule("-----");
  581. PutModule("If you do not include an argument on items that supports arguments, LogMySQL will return the current setting for the item.");
  582. } else if (sCommand.Equals("HOST")) {
  583. if (sArgs.empty()) {
  584. CString host = GetNV("host");
  585. PutModule("Host is set to: "+host);
  586. } else {
  587. SetNV("host",sArgs);
  588. PutModule("Host is now set to: "+sArgs);
  589. }
  590. } else if (sCommand.Equals("PORT")) {
  591. if (sArgs.empty()) {
  592. CString port = GetNV("port");
  593. PutModule("Port is set to: "+port);
  594. } else {
  595. CString result;
  596. unsigned int port = strtoul(sArgs.c_str(), NULL, 10);
  597. char portStr[20];
  598. snprintf(portStr, sizeof(portStr), "%u", port);
  599. result = portStr;
  600. SetNV("port",result);
  601. PutModule("Port is now set to: "+result);
  602. }
  603. } else if (sCommand.Equals("USERNAME")) {
  604. if (sArgs.empty()) {
  605. CString username = GetNV("username");
  606. PutModule("Username is set to: "+username);
  607. } else {
  608. SetNV("username",sArgs);
  609. PutModule("Username is now set to: "+sArgs);
  610. }
  611. } else if (sCommand.Equals("PASSWORD")) {
  612. if (sArgs.empty()) {
  613. CString password = GetNV("password");
  614. if (password.empty())
  615. PutModule("Password is blank.");
  616. else
  617. PutModule("Password is set.");
  618. } else {
  619. SetNV("password",sArgs);
  620. PutModule("Password is now set.");
  621. }
  622. } else if (sCommand.Equals("DATABASENAME")) {
  623. if (sArgs.empty()) {
  624. CString databaseName = GetNV("databaseName");
  625. PutModule("Database Name is set to: "+databaseName);
  626. } else {
  627. SetNV("databaseName",sArgs);
  628. PutModule("Database Name is now set to: "+sArgs);
  629. }
  630. } else if (sCommand.Equals("CONNECT")) {
  631. MySQLConnect();
  632. if (databaseConnected)
  633. PutModule("Database is now connected");
  634. else
  635. PutModule("Unable to connect to database. Check configuration.");
  636. } else if (sCommand.Equals("REPLAY")) {
  637. Replay();
  638. PutModule("Replayed");
  639. } else if (sCommand.Equals("REPLAYALL")) {
  640. if (sArgs.empty()) {
  641. CString status = (replayAll ? "On" : "Off");
  642. PutModule("ReplayAll is set to: "+status);
  643. } else {
  644. if (sArgs.Equals("ON") || sArgs.Equals("1") || sArgs.Equals("true")) {
  645. replayAll = true;
  646. SetSetting("replayAll", "1");
  647. } else {
  648. replayAll = false;
  649. SetSetting("replayAll", "0");
  650. }
  651. CString status = (replayAll ? "On" : "Off");
  652. PutModule("ReplayAll is now set to: "+status);
  653. }
  654. } else if (sCommand.Equals("LOGLIMIT")) {
  655. if (sArgs.empty()) {
  656. CString result;
  657. char limitStr[20];
  658. snprintf(limitStr, sizeof(limitStr), "%lu", logLimit);
  659. result = limitStr;
  660. PutModule("LogLimit is set to: "+result);
  661. } else {
  662. logLimit = strtoul(sArgs.c_str(), NULL, 10);
  663. CString result;
  664. char limitStr[20];
  665. snprintf(limitStr, sizeof(limitStr), "%lu", logLimit);
  666. result = limitStr;
  667. SetSetting("logLimit", result);
  668. PutModule("LogLimit is now set to: "+result);
  669. }
  670. } else if (sCommand.Equals("LOGLEVEL")) {
  671. if (sArgs.empty()) {
  672. CString result;
  673. char levelStr[20];
  674. snprintf(levelStr, sizeof(levelStr), "%d", logLevel);
  675. result = levelStr;
  676. PutModule("LogLevel is set to: "+result);
  677. } else {
  678. logLevel = atoi(sArgs.c_str());
  679. CString result;
  680. char levelStr[20];
  681. snprintf(levelStr, sizeof(levelStr), "%d", logLevel);
  682. result = levelStr;
  683. SetSetting("logLevel", result);
  684. PutModule("LogLevel is now set to: "+result);
  685. }
  686. } else
  687. PutModule("Unknown command ["+sCommand+"] for help, type help.");
  688. }
  689. void Replay() {
  690. if (!databaseConnected)
  691. return;
  692. PutUser(":*LogMySQL!LogMySQL@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Buffer Playback...");
  693. CString lastOnline = GetSetting("clientDisconnected");
  694. MYSQL_STMT *statement = mysql_stmt_init(database);
  695. int status = 0;
  696. if (!replayAll && !lastOnline.empty()) {
  697. status = mysql_stmt_prepare(statement, "SELECT * FROM `messages` WHERE `time`>? ORDER BY `time`", 55);
  698. if (status!=0) {
  699. PutUser(":*LogMySQL!LogMySQL@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Playback failed due to sql problem.");
  700. return;
  701. }
  702. MYSQL_BIND bind[1];
  703. memset(bind, 0, sizeof(bind));
  704. bind[0].buffer_type = MYSQL_TYPE_STRING;
  705. bind[0].buffer = (void*)lastOnline.c_str();
  706. bind[0].buffer_length = lastOnline.length();
  707. bind[0].is_null = false;
  708. status = mysql_stmt_bind_param(statement, bind);
  709. if (status!=0) {
  710. PutUser(":*LogMySQL!LogMySQL@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Playback failed due to sql problem.");
  711. mysql_stmt_close(statement);
  712. return;
  713. }
  714. } else {
  715. status = mysql_stmt_prepare(statement, "SELECT * FROM `messages` ORDER BY `time`", 40);
  716. if (status!=0) {
  717. PutUser(":*LogMySQL!LogMySQL@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Playback failed due to sql problem.");
  718. return;
  719. }
  720. }
  721. status = mysql_stmt_execute(statement);
  722. if (status!=0) {
  723. PutUser(":*LogMySQL!LogMySQL@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Playback failed due to sql problem.");
  724. mysql_stmt_close(statement);
  725. return;
  726. }
  727. MYSQL_RES *result = mysql_stmt_result_metadata(statement);
  728. unsigned int columnCount = mysql_num_fields(result);
  729. MYSQL_FIELD *fields = mysql_fetch_fields(result);
  730. MYSQL_BIND results[columnCount];
  731. memset(results, 0, sizeof(results));
  732. map<unsigned int,char *> columnData;
  733. map<unsigned int,unsigned long> columnLength;
  734. map<unsigned int,bool> columnNull;
  735. map<unsigned int,CString> columns;
  736. for (unsigned int i=0; i<columnCount; i++) {
  737. columns[i] = CString(fields[i].name);
  738. if (columns[i].empty()) {
  739. char count[20];
  740. snprintf(count, sizeof(count), "%u", i);
  741. columns[i] = count;
  742. }
  743. columnData[i] = (char *)malloc(fields[i].length);
  744. results[i].buffer_type = MYSQL_TYPE_STRING;
  745. results[i].buffer = (void *)columnData[i];
  746. results[i].buffer_length = fields[i].length;
  747. results[i].length = &columnLength[i];
  748. results[i].is_null = (my_bool *)&columnNull[i];
  749. }
  750. status = mysql_stmt_bind_result(statement, results);
  751. if (status!=0) {
  752. PutUser(":*LogMySQL!LogMySQL@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Playback failed due to sql problem.");
  753. mysql_free_result(result);
  754. mysql_stmt_close(statement);
  755. return;
  756. }
  757. while (true) {
  758. status = mysql_stmt_fetch(statement);
  759. if (status!=0)
  760. break;
  761. map<CString,CString> data;
  762. for (unsigned int i=0; i<columnCount; i++) {
  763. if (columnNull[i]) {
  764. data[columns[i]] = "";
  765. } else {
  766. data[columns[i]] = CString(columnData[i], columnLength[i]);
  767. }
  768. }
  769. time_t now = time(NULL);
  770. time_t unixTime = (time_t)strtol(data["time"].c_str(),NULL,10);
  771. struct tm *timeinfo = localtime(&unixTime);
  772. char timeStr[20];
  773. if (((long)now-86000)<(long)time)
  774. strftime(timeStr, sizeof(timeStr), "%I:%M:%S %p", timeinfo);
  775. else
  776. strftime(timeStr, sizeof(timeStr), "%m/%d/%y %I:%M:%S %p", timeinfo);
  777. if (data["type"].Equals("DISCONNECT")) {
  778. PutUser(":*LogMySQL!LogMySQL@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :["+timeStr+"] Server Disconnected");
  779. } else if (data["type"].Equals("CONNECT")) {
  780. PutUser(":*LogMySQL!LogMySQL@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :["+timeStr+"] Server Connected");
  781. } else if (data["type"].Equals("JOIN")) {
  782. PutUser(":"+data["nick"]+" NOTICE "+data["target"]+" :["+timeStr+"] joined");
  783. } else if (data["type"].Equals("PART")) {
  784. if (data["message"].Equals(""))
  785. PutUser(":"+data["nick"]+" NOTICE "+data["target"]+" :["+timeStr+"] parted");
  786. else
  787. PutUser(":"+data["nick"]+" NOTICE "+data["target"]+" :["+timeStr+"] parted: "+data["message"]);
  788. } else if (data["type"].Equals("TOPIC")) {
  789. PutUser(":"+data["nick"]+" NOTICE "+data["target"]+" :["+timeStr+"] changed topic: "+data["message"]);
  790. } else if (data["type"].Equals("QUIT")) {
  791. if (data["message"].Equals(""))
  792. PutUser(":"+data["nick"]+" NOTICE "+data["target"]+" :["+timeStr+"] quit");
  793. else
  794. PutUser(":"+data["nick"]+" NOTICE "+data["target"]+" :["+timeStr+"] quit: "+data["message"]);
  795. } else if (data["type"].Equals("MODE")) {
  796. PutUser(":"+data["nick"]+" NOTICE "+data["target"]+" :["+timeStr+"] changed mode: "+data["message"]);
  797. } else if (data["type"].Equals("ACTION")) {
  798. PutUser(":"+data["nick"]+" PRIVMSG "+data["target"]+" :\001ACTION ["+timeStr+"] "+data["message"]+"\001");
  799. } else {
  800. PutUser(":"+data["nick"]+" "+data["type"]+" "+data["target"]+" :["+timeStr+"] "+data["message"]);
  801. }
  802. }
  803. for (unsigned int i=0; i<columnCount; i++) {
  804. free(columnData[i]);
  805. }
  806. mysql_free_result(result);
  807. mysql_stmt_close(statement);
  808. if (logLimit==1) {
  809. statement = mysql_stmt_init(database);
  810. status = mysql_stmt_prepare(statement, "DELETE FROM `messages`", 22);
  811. if (status==0) {
  812. mysql_stmt_execute(statement);
  813. mysql_stmt_close(statement);
  814. }
  815. }
  816. PutUser(":*LogMySQL!LogMySQL@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Playback Complete.");
  817. }
  818. private:
  819. MYSQL *database;
  820. bool databaseConnected;
  821. bool connected;
  822. bool replayAll;
  823. unsigned long logLimit;
  824. int logLevel;
  825. };
  826. template<> void TModInfo<CLogMySQL>(CModInfo& Info) {
  827. Info.SetWikiPage("logmysql");
  828. }
  829. NETWORKMODULEDEFS(CLogMySQL, "Add logging to MySQL")