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.

903 lines
35 KiB

13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
  1. //
  2. // logsqlite.cpp
  3. // LogSQL
  4. //
  5. // Created by Mr. Gecko on 2/3/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. #include <sqlite3.h>
  25. class CLogSQLite : public CModule {
  26. public:
  27. MODCONSTRUCTOR(CLogSQLite) {
  28. AddHelpCommand();
  29. AddCommand("Replay", static_cast<CModCommand::ModCmdFunc>(&CLogSQLite::ReplayCommand), "", "Play back the messages received.");
  30. AddCommand("ReplayAll", static_cast<CModCommand::ModCmdFunc>(&CLogSQLite::ReplayAllCommand), "[1|0]", "Replay all messages stored.");
  31. AddCommand("LogLimit", static_cast<CModCommand::ModCmdFunc>(&CLogSQLite::LogLimitCommand), "[0-9]+", "Limit the amount of items to store into the log.");
  32. AddCommand("LogLevel", static_cast<CModCommand::ModCmdFunc>(&CLogSQLite::LogLevelCommand), "[0-4]", "Log level.");
  33. AddCommand("AddIgnore", static_cast<CModCommand::ModCmdFunc>(&CLogSQLite::AddIgnoreCommand), "Type[nick|chan] Target", "Add to ignore list.");
  34. AddCommand("RemoveIgnore", static_cast<CModCommand::ModCmdFunc>(&CLogSQLite::RemoveIgnoreCommand), "Type[nick|chan] Target", "Remove from ignore list.");
  35. AddCommand("IgnoreList", static_cast<CModCommand::ModCmdFunc>(&CLogSQLite::IgnoreListCommand), "", "View what is currently ignored.");
  36. }
  37. void ReplayCommand(const CString &sLine) {
  38. Replay();
  39. PutModule("Replayed");
  40. }
  41. void ReplayAllCommand(const CString &sLine) {
  42. CString sArgs = sLine.Token(1, true);
  43. bool help = sArgs.Equals("HELP");
  44. if (help) {
  45. PutModule("On: All logs stored will be replayed.");
  46. PutModule("Off: Only logs since the last time you connected will be replayed.");
  47. } else if (!sArgs.empty()) {
  48. replayAll = sArgs.ToBool();
  49. SetSetting("replayAll", (replayAll ? "1" : "0"));
  50. }
  51. CString status = (replayAll ? "On" : "Off");
  52. CString now = (sArgs.empty() || help ? "" : "now ");
  53. PutModule("ReplayAll is "+now+"set to: "+=status);
  54. }
  55. void LogLimitCommand(const CString &sLine) {
  56. CString sArgs = sLine.Token(1, true);
  57. bool help = sArgs.Equals("HELP");
  58. CString setting;
  59. if (help) {
  60. PutModule("0: Everything will be kept in database.");
  61. PutModule("1: Everything will be kept in database until replayed.");
  62. PutModule("2+: Limit logs stored in database to limit set.");
  63. } else if (!sArgs.empty()) {
  64. logLimit = strtoul(sArgs.c_str(), NULL, 10);
  65. char limitStr[20];
  66. snprintf(limitStr, sizeof(limitStr), "%lu", logLimit);
  67. setting = limitStr;
  68. SetSetting("logLimit", setting);
  69. }
  70. if (setting.empty()) {
  71. char limitStr[20];
  72. snprintf(limitStr, sizeof(limitStr), "%lu", logLimit);
  73. setting = limitStr;
  74. }
  75. CString now = (sArgs.empty() || help ? "" : "now ");
  76. PutModule("LogLimit is "+now+"set to: "+setting);
  77. }
  78. void LogLevelCommand(const CString &sLine) {
  79. CString sArgs = sLine.Token(1, true);
  80. bool help = sArgs.Equals("HELP");
  81. CString setting;
  82. if (help) {
  83. PutModule("0: Mentions and messages to you.");
  84. PutModule("1: All messages.");
  85. PutModule("2: Actions, Joins/Parts, and Notices.");
  86. PutModule("3: Server wide messages.");
  87. PutModule("4: All messages, actions, joins/parts, and noticies sent by you.");
  88. } else if (!sArgs.empty()) {
  89. logLevel = atoi(sArgs.c_str());
  90. char levelStr[20];
  91. snprintf(levelStr, sizeof(levelStr), "%d", logLevel);
  92. setting = levelStr;
  93. SetSetting("logLevel", setting);
  94. }
  95. if (setting.empty()) {
  96. char levelStr[20];
  97. snprintf(levelStr, sizeof(levelStr), "%d", logLevel);
  98. setting = levelStr;
  99. }
  100. CString now = (sArgs.empty() || help ? "" : "now ");
  101. PutModule("LogLevel is "+now+"set to: "+setting);
  102. }
  103. void AddIgnoreCommand(const CString &sLine) {
  104. CString type = sLine.Token(1);
  105. CString target = sLine.Token(2);
  106. bool help = sLine.Equals("HELP");
  107. if (help) {
  108. PutModule("Inorder to add an ignore, you must choose what type of ignore it is which can ether be a nick or a chan.");
  109. PutModule("Nicks are matched with wildcards against the full mask.");
  110. PutModule("Channels are matched by #channel which can contain wildcards.");
  111. } else if (!type.empty() && !target.empty()) {
  112. if (type.Equals("nick"))
  113. type = "nick";
  114. else if (type.Equals("chan") || type.Equals("channel"))
  115. type = "chan";
  116. else
  117. type = "";
  118. if (type.empty()) {
  119. PutModule("Unknown type. If you need help, type \"AddIgnore help\".");
  120. return;
  121. }
  122. if (AddIgnore(type, target))
  123. PutModule("Successfully added \""+target+"\" to the ignore list.");
  124. else
  125. PutModule("Failed, maybe it already existed?");
  126. } else {
  127. PutModule("If you need help, type \"AddIgnore help\".");
  128. }
  129. }
  130. void RemoveIgnoreCommand(const CString &sLine) {
  131. CString type = sLine.Token(1);
  132. CString target = sLine.Token(2);
  133. bool help = sLine.Equals("HELP");
  134. if (help) {
  135. PutModule("Inorder to remove an ignore, you must specify the type and the exact pattren used to add it. If you need to find what currently exists, type \"IgnoreList\".");
  136. } else if (!type.empty() && !target.empty()) {
  137. if (type.Equals("nick"))
  138. type = "nick";
  139. else if (type.Equals("chan") || type.Equals("channel"))
  140. type = "chan";
  141. else
  142. type = "";
  143. if (type.empty()) {
  144. PutModule("Unknown type. If you need help, type \"RemoveIgnore help\".");
  145. return;
  146. }
  147. if (RemoveIgnore(type, target))
  148. PutModule("Successfully removed \""+target+"\" from the ignore list.");
  149. else
  150. PutModule("Failed, maybe it does not exist?");
  151. } else {
  152. PutModule("If you need help, type \"RemoveIgnore help\".");
  153. }
  154. }
  155. void IgnoreListCommand(const CString &sLine) {
  156. if (nickIgnoreList.size()==0) {
  157. PutModule("The nick ignore list is currently empty.");
  158. } else {
  159. PutModule("Nick ignore list contains:");
  160. for (vector<CString>::iterator it=nickIgnoreList.begin(); it<nickIgnoreList.end(); it++) {
  161. PutModule(*it);
  162. }
  163. }
  164. PutModule("---");
  165. if (chanIgnoreList.size()==0) {
  166. PutModule("The channel ignore list is currently empty.");
  167. } else {
  168. PutModule("Channel ignore list contains:");
  169. for (vector<CString>::iterator it=chanIgnoreList.begin(); it<chanIgnoreList.end(); it++) {
  170. PutModule(*it);
  171. }
  172. }
  173. }
  174. virtual bool OnLoad(const CString& sArgs, CString& sMessage) {
  175. connected = true;
  176. CString savePath = GetSavePath();
  177. savePath += "/log.sqlite";
  178. sqlite3_open(savePath.c_str(), &database);
  179. sqlite3_stmt *result;
  180. int status = sqlite3_prepare(database, "SELECT `name` FROM `sqlite_master` WHERE `name`='settings'", -1, &result, NULL);
  181. if (status==SQLITE_OK) {
  182. status = SQLITE_BUSY;
  183. while (status==SQLITE_BUSY) {
  184. status = sqlite3_step(result);
  185. }
  186. sqlite3_finalize(result);
  187. if (status!=SQLITE_ROW) {
  188. status = sqlite3_prepare(database, "CREATE TABLE `settings` (`name` text, `value` text)", -1, &result, NULL);
  189. if (status==SQLITE_OK) {
  190. sqlite3_step(result);
  191. sqlite3_finalize(result);
  192. SetSetting("replayAll","0");
  193. SetSetting("logLimit","1");
  194. SetSetting("logLevel","1");
  195. SetSetting("version","1");
  196. }
  197. }
  198. }
  199. status = sqlite3_prepare(database, "SELECT `name` FROM `sqlite_master` WHERE `name`='messages'", -1, &result, NULL);
  200. if (status==SQLITE_OK) {
  201. status = SQLITE_BUSY;
  202. while (status==SQLITE_BUSY) {
  203. status = sqlite3_step(result);
  204. }
  205. sqlite3_finalize(result);
  206. if (status!=SQLITE_ROW) {
  207. status = sqlite3_prepare(database, "CREATE TABLE `messages` (`target` text, `nick` text, `type` text, `message` text, `time` real(20,5))", -1, &result, NULL);
  208. if (status==SQLITE_OK) {
  209. sqlite3_step(result);
  210. sqlite3_finalize(result);
  211. }
  212. }
  213. }
  214. status = sqlite3_prepare(database, "SELECT `name` FROM `sqlite_master` WHERE `name`='ignorelist'", -1, &result, NULL);
  215. if (status==SQLITE_OK) {
  216. status = SQLITE_BUSY;
  217. while (status==SQLITE_BUSY) {
  218. status = sqlite3_step(result);
  219. }
  220. sqlite3_finalize(result);
  221. if (status!=SQLITE_ROW) {
  222. status = sqlite3_prepare(database, "CREATE TABLE `ignorelist` (`type` text, `target` text)", -1, &result, NULL);
  223. if (status==SQLITE_OK) {
  224. sqlite3_step(result);
  225. sqlite3_finalize(result);
  226. }
  227. }
  228. }
  229. replayAll = atoi(GetSetting("replayAll").c_str());
  230. logLimit = strtoul(GetSetting("logLimit").c_str(), NULL, 10);
  231. logLevel = atoi(GetSetting("logLevel").c_str());
  232. unsigned long version = strtoul(GetSetting("version").c_str(), NULL, 10);
  233. if (version==0)
  234. SetSetting("version","1");
  235. UpdateIgnoreLists();
  236. return true;
  237. }
  238. virtual ~CLogSQLite() {
  239. int result = sqlite3_close(database);
  240. if (result==SQLITE_BUSY) {
  241. sqlite3_stmt *stmt;
  242. while ((stmt = sqlite3_next_stmt(database, NULL))!=NULL) {
  243. sqlite3_finalize(stmt);
  244. }
  245. result = sqlite3_close(database);
  246. if (result!=SQLITE_OK)
  247. printf("Unable to close the SQLite Database\n");
  248. }
  249. }
  250. CString GetUNIXTime() {
  251. struct timeval time;
  252. gettimeofday(&time, NULL);
  253. double microtime = (double)(time.tv_sec + (time.tv_usec/1000000.00));
  254. char timeStr[25];
  255. snprintf(timeStr, sizeof(timeStr), "%f", microtime);
  256. return timeStr;
  257. }
  258. void AddMessage(const CString& target, const CString& nick, const CString& type, const CString& message) {
  259. if (IsIgnored("nick",nick) || (target.Left(1).Equals("#") && IsIgnored("chan",target)))
  260. return;
  261. sqlite3_stmt *result;
  262. int status = sqlite3_prepare(database, "INSERT INTO `messages` (`target`, `nick`, `type`, `message`, `time`) VALUES (?,?,?,?,?)", -1, &result, NULL);
  263. if (status!=SQLITE_OK)
  264. return;
  265. status = sqlite3_bind_text(result, 1, target.c_str(), target.length(), SQLITE_STATIC);
  266. if (status!=SQLITE_OK) {
  267. sqlite3_finalize(result);
  268. return;
  269. }
  270. status = sqlite3_bind_text(result, 2, nick.c_str(), nick.length(), SQLITE_STATIC);
  271. if (status!=SQLITE_OK) {
  272. sqlite3_finalize(result);
  273. return;
  274. }
  275. status = sqlite3_bind_text(result, 3, type.c_str(), type.length(), SQLITE_STATIC);
  276. if (status!=SQLITE_OK) {
  277. sqlite3_finalize(result);
  278. return;
  279. }
  280. status = sqlite3_bind_text(result, 4, message.c_str(), message.length(), SQLITE_STATIC);
  281. if (status!=SQLITE_OK) {
  282. sqlite3_finalize(result);
  283. return;
  284. }
  285. CString time = GetUNIXTime();
  286. status = sqlite3_bind_text(result, 5, time.c_str(), time.length(), SQLITE_STATIC);
  287. if (status!=SQLITE_OK) {
  288. sqlite3_finalize(result);
  289. return;
  290. }
  291. sqlite3_step(result);
  292. sqlite3_finalize(result);
  293. if (logLimit>=2) {
  294. status = sqlite3_prepare(database, "SELECT COUNT(*) FROM `messages`", -1, &result, NULL);
  295. if (status!=SQLITE_OK)
  296. return;
  297. status = SQLITE_BUSY;
  298. while (status==SQLITE_BUSY) {
  299. status = sqlite3_step(result);
  300. }
  301. if (status!=SQLITE_ROW)
  302. return;
  303. int dataCount = sqlite3_data_count(result);
  304. if (dataCount!=1) {
  305. sqlite3_finalize(result);
  306. cout << "LogSQLite: We are only suppose to receive 1 field. We received the count of " << dataCount << " fields.\n";
  307. return;
  308. }
  309. unsigned long count = strtoul((const char *)sqlite3_column_text(result, 0), NULL, 10);
  310. if (count<=logLimit)
  311. count = 0;
  312. else
  313. count = count-logLimit;
  314. sqlite3_finalize(result);
  315. if (count!=0) {
  316. char *queryStr = (char *)malloc(73);
  317. snprintf(queryStr, 73, "SELECT `rowid` FROM `messages` ORDER BY `time` LIMIT %lu", count);
  318. status = sqlite3_prepare(database, queryStr, -1, &result, NULL);
  319. free(queryStr);
  320. if (status!=SQLITE_OK)
  321. return;
  322. while (true) {
  323. status = SQLITE_BUSY;
  324. while (status==SQLITE_BUSY) {
  325. status = sqlite3_step(result);
  326. }
  327. if (status!=SQLITE_ROW)
  328. break;
  329. dataCount = sqlite3_data_count(result);
  330. if (dataCount!=1) {
  331. sqlite3_finalize(result);
  332. cout << "LogSQLite: We are only suppose to receive 1 field. We received the count of " << dataCount << " fields.\n";
  333. break;
  334. }
  335. sqlite3_stmt *result2;
  336. const char *rowid = (const char *)sqlite3_column_text(result, 0);
  337. status = sqlite3_prepare(database, "DELETE FROM `messages` WHERE `rowid`=?", -1, &result2, NULL);
  338. if (status!=SQLITE_OK)
  339. continue;
  340. status = sqlite3_bind_text(result2, 1, rowid, strlen(rowid), SQLITE_STATIC);
  341. if (status!=SQLITE_OK) {
  342. sqlite3_finalize(result2);
  343. continue;
  344. }
  345. sqlite3_step(result2);
  346. sqlite3_finalize(result2);
  347. }
  348. sqlite3_finalize(result);
  349. }
  350. }
  351. }
  352. void SetSetting(const CString& name, const CString& value) {
  353. bool exists = false;
  354. sqlite3_stmt *result;
  355. int status = sqlite3_prepare(database, "SELECT `value` FROM `settings` WHERE `name`=?", -1, &result, NULL);
  356. if (status==SQLITE_OK) {
  357. status = sqlite3_bind_text(result, 1, name.c_str(), name.length(), SQLITE_STATIC);
  358. if (status==SQLITE_OK) {
  359. status = SQLITE_BUSY;
  360. while (status==SQLITE_BUSY) {
  361. status = sqlite3_step(result);
  362. }
  363. if (status==SQLITE_ROW) {
  364. exists = true;
  365. }
  366. }
  367. sqlite3_finalize(result);
  368. }
  369. if (exists) {
  370. status = sqlite3_prepare(database, "UPDATE `settings` SET `value`=? WHERE `name`=?", -1, &result, NULL);
  371. if (status!=SQLITE_OK) {
  372. return;
  373. }
  374. status = sqlite3_bind_text(result, 1, value.c_str(), value.length(), SQLITE_STATIC);
  375. if (status!=SQLITE_OK) {
  376. sqlite3_finalize(result);
  377. return;
  378. }
  379. status = sqlite3_bind_text(result, 2, name.c_str(), name.length(), SQLITE_STATIC);
  380. if (status!=SQLITE_OK) {
  381. sqlite3_finalize(result);
  382. return;
  383. }
  384. sqlite3_step(result);
  385. sqlite3_finalize(result);
  386. } else {
  387. status = sqlite3_prepare(database, "INSERT INTO `settings` (`name`,`value`) VALUES (?,?)", -1, &result, NULL);
  388. if (status!=SQLITE_OK) {
  389. return;
  390. }
  391. status = sqlite3_bind_text(result, 1, name.c_str(), name.length(), SQLITE_STATIC);
  392. if (status!=SQLITE_OK) {
  393. sqlite3_finalize(result);
  394. return;
  395. }
  396. status = sqlite3_bind_text(result, 2, value.c_str(), value.length(), SQLITE_STATIC);
  397. if (status!=SQLITE_OK) {
  398. sqlite3_finalize(result);
  399. return;
  400. }
  401. sqlite3_step(result);
  402. sqlite3_finalize(result);
  403. }
  404. }
  405. CString GetSetting(const CString& name) {
  406. CString stringValue;
  407. sqlite3_stmt *result;
  408. int status = sqlite3_prepare(database, "SELECT `value` FROM `settings` WHERE `name`=?", -1, &result, NULL);
  409. if (status!=SQLITE_OK)
  410. return stringValue;
  411. status = sqlite3_bind_text(result, 1, name.c_str(), name.length(), SQLITE_STATIC);
  412. if (status!=SQLITE_OK) {
  413. sqlite3_finalize(result);
  414. return stringValue;
  415. }
  416. while (true) {
  417. status = SQLITE_BUSY;
  418. while (status==SQLITE_BUSY) {
  419. status = sqlite3_step(result);
  420. }
  421. if (status!=SQLITE_ROW)
  422. break;
  423. int dataCount = sqlite3_data_count(result);
  424. if (dataCount!=1) {
  425. sqlite3_finalize(result);
  426. cout << "LogSQLite: Settings are only suppose to return 1 as the field count. We received the count of " << dataCount << " fields.\n";
  427. return stringValue;
  428. }
  429. stringValue = CString((const char *)sqlite3_column_text(result, 0));
  430. break;
  431. }
  432. sqlite3_finalize(result);
  433. return stringValue;
  434. }
  435. void UpdateIgnoreLists() {
  436. nickIgnoreList.clear();
  437. sqlite3_stmt *result;
  438. int status = sqlite3_prepare(database, "SELECT `target` FROM `ignorelist` WHERE `type`='nick'", -1, &result, NULL);
  439. if (status==SQLITE_OK) {
  440. while (true) {
  441. status = SQLITE_BUSY;
  442. while (status==SQLITE_BUSY) {
  443. status = sqlite3_step(result);
  444. }
  445. if (status!=SQLITE_ROW)
  446. break;
  447. int dataCount = sqlite3_data_count(result);
  448. if (dataCount!=1)
  449. break;
  450. CString ignore = CString((const char *)sqlite3_column_text(result, 0));
  451. nickIgnoreList.push_back(ignore);
  452. }
  453. sqlite3_finalize(result);
  454. }
  455. chanIgnoreList.clear();
  456. status = sqlite3_prepare(database, "SELECT `target` FROM `ignorelist` WHERE `type`='chan'", -1, &result, NULL);
  457. if (status==SQLITE_OK) {
  458. while (true) {
  459. status = SQLITE_BUSY;
  460. while (status==SQLITE_BUSY) {
  461. status = sqlite3_step(result);
  462. }
  463. if (status!=SQLITE_ROW)
  464. break;
  465. int dataCount = sqlite3_data_count(result);
  466. if (dataCount!=1)
  467. break;
  468. CString ignore = CString((const char *)sqlite3_column_text(result, 0));
  469. chanIgnoreList.push_back(ignore);
  470. }
  471. sqlite3_finalize(result);
  472. }
  473. }
  474. bool AddIgnore(const CString& type, const CString& target) {
  475. if (!IgnoreExists(type, target)) {
  476. sqlite3_stmt *result;
  477. int status = sqlite3_prepare(database, "INSERT INTO `ignorelist` (`type`, `target`) VALUES (?,?)", -1, &result, NULL);
  478. if (status!=SQLITE_OK)
  479. return false;
  480. status = sqlite3_bind_text(result, 1, type.c_str(), type.length(), SQLITE_STATIC);
  481. if (status!=SQLITE_OK) {
  482. sqlite3_finalize(result);
  483. return false;
  484. }
  485. status = sqlite3_bind_text(result, 2, target.c_str(), target.length(), SQLITE_STATIC);
  486. if (status!=SQLITE_OK) {
  487. sqlite3_finalize(result);
  488. return false;
  489. }
  490. sqlite3_step(result);
  491. sqlite3_finalize(result);
  492. if (type.Equals("nick"))
  493. nickIgnoreList.push_back(target);
  494. else if (type.Equals("chan"))
  495. chanIgnoreList.push_back(target);
  496. return true;
  497. }
  498. return false;
  499. }
  500. bool RemoveIgnore(const CString& type, const CString& target) {
  501. if (IgnoreExists(type, target)) {
  502. sqlite3_stmt *result;
  503. int status = sqlite3_prepare(database, "DELETE FROM `ignorelist` WHERE `type`=? AND `target`=?", -1, &result, NULL);
  504. if (status!=SQLITE_OK)
  505. return false;
  506. status = sqlite3_bind_text(result, 1, type.c_str(), type.length(), SQLITE_STATIC);
  507. if (status!=SQLITE_OK) {
  508. sqlite3_finalize(result);
  509. return false;
  510. }
  511. status = sqlite3_bind_text(result, 2, target.c_str(), target.length(), SQLITE_STATIC);
  512. if (status!=SQLITE_OK) {
  513. sqlite3_finalize(result);
  514. return false;
  515. }
  516. sqlite3_step(result);
  517. sqlite3_finalize(result);
  518. UpdateIgnoreLists();
  519. return true;
  520. }
  521. return false;
  522. }
  523. bool IgnoreExists(const CString& type, const CString& target) {
  524. if (type.Equals("nick")) {
  525. for (vector<CString>::iterator it=nickIgnoreList.begin(); it<nickIgnoreList.end(); it++) {
  526. if (target.Equals(*it))
  527. return true;
  528. }
  529. } else if (type.Equals("chan")) {
  530. for (vector<CString>::iterator it=chanIgnoreList.begin(); it<chanIgnoreList.end(); it++) {
  531. if (target.Equals(*it))
  532. return true;
  533. }
  534. }
  535. return false;
  536. }
  537. bool IsIgnored(const CString& type, const CString& target) {
  538. if (type.Equals("nick")) {
  539. for (vector<CString>::iterator it=nickIgnoreList.begin(); it<nickIgnoreList.end(); it++) {
  540. if (target.WildCmp(*it))
  541. return true;
  542. }
  543. } else if (type.Equals("chan")) {
  544. for (vector<CString>::iterator it=chanIgnoreList.begin(); it<chanIgnoreList.end(); it++) {
  545. if (target.WildCmp(*it))
  546. return true;
  547. }
  548. }
  549. return false;
  550. }
  551. //Server stuff
  552. virtual void OnIRCDisconnected() {
  553. if (connected) {
  554. connected = false;
  555. if (logLevel>=3) {
  556. AddMessage("","","DISCONNECT","");
  557. }
  558. }
  559. }
  560. virtual void OnIRCConnected() {
  561. if (!connected) {
  562. connected = true;
  563. if (logLevel>=3) {
  564. AddMessage("","","CONNECT","");
  565. }
  566. }
  567. }
  568. //User stuff
  569. virtual EModRet OnUserAction(CString& sTarget, CString& sMessage) {
  570. if (logLevel>=4) {
  571. AddMessage(sTarget,m_pNetwork->GetIRCNick().GetNickMask(),"ACTION",sMessage);
  572. }
  573. return CONTINUE;
  574. }
  575. virtual EModRet OnUserMsg(CString& sTarget, CString& sMessage) {
  576. if (logLevel>=4) {
  577. AddMessage(sTarget,m_pNetwork->GetIRCNick().GetNickMask(),"PRIVMSG",sMessage);
  578. }
  579. return CONTINUE;
  580. }
  581. virtual EModRet OnUserNotice(CString& sTarget, CString& sMessage) {
  582. if (logLevel>=4) {
  583. AddMessage(sTarget,m_pNetwork->GetIRCNick().GetNickMask(),"NOTICE",sMessage);
  584. }
  585. return CONTINUE;
  586. }
  587. virtual EModRet OnUserJoin(CString& sChannel, CString& sKey) {
  588. if (logLevel>=4) {
  589. AddMessage(sChannel,m_pNetwork->GetIRCNick().GetNickMask(),"JOIN","");
  590. }
  591. return CONTINUE;
  592. }
  593. virtual EModRet OnUserPart(CString& sChannel, CString& sMessage) {
  594. if (logLevel>=4) {
  595. AddMessage(sChannel,m_pNetwork->GetIRCNick().GetNickMask(),"PART",sMessage);
  596. }
  597. return CONTINUE;
  598. }
  599. virtual EModRet OnUserTopic(CString& sChannel, CString& sTopic) {
  600. if (logLevel>=4) {
  601. AddMessage(sChannel,m_pNetwork->GetIRCNick().GetNickMask(),"TOPIC",sTopic);
  602. }
  603. return CONTINUE;
  604. }
  605. //Other stuff
  606. virtual void OnRawMode(const CNick& OpNick, CChan& Channel, const CString& sModes, const CString& sArgs) {
  607. if (logLevel>=3) {
  608. AddMessage(Channel.GetName(),OpNick.GetNickMask(),"MODE",sModes+" "+sArgs);
  609. }
  610. }
  611. virtual void OnKick(const CNick& OpNick, const CString& sKickedNick, CChan& Channel, const CString& sMessage) {
  612. if (logLevel>=2) {
  613. AddMessage(Channel.GetName(),OpNick.GetNickMask(),"KICK",sKickedNick+" "+sMessage);
  614. }
  615. }
  616. virtual void OnQuit(const CNick& Nick, const CString& sMessage, const vector<CChan*>& vChans) {
  617. if (logLevel>=2) {
  618. vector<CChan*>::const_iterator it;
  619. for (it=vChans.begin(); it!=vChans.end(); it++) {
  620. CChan& channel = **it;
  621. AddMessage(channel.GetName(),Nick.GetNickMask(),"QUIT",sMessage);
  622. }
  623. }
  624. }
  625. virtual void OnJoin(const CNick& Nick, CChan& Channel) {
  626. if (logLevel>=2) {
  627. AddMessage(Channel.GetName(),Nick.GetNickMask(),"JOIN","");
  628. }
  629. }
  630. virtual void OnPart(const CNick& Nick, CChan& Channel, const CString& sMessage) {
  631. if (logLevel>=2) {
  632. AddMessage(Channel.GetName(),Nick.GetNickMask(),"JOIN",sMessage);
  633. }
  634. }
  635. virtual void OnNick(const CNick& OldNick, const CString& sNewNick, const vector<CChan*>& vChans) {
  636. if (logLevel>=2) {
  637. vector<CChan*>::const_iterator it;
  638. for (it=vChans.begin(); it!=vChans.end(); it++) {
  639. CChan& channel = **it;
  640. AddMessage(channel.GetName(),OldNick.GetNickMask(),"NICK",sNewNick);
  641. }
  642. }
  643. }
  644. virtual EModRet OnPrivAction(CNick& Nick, CString& sMessage) {
  645. if (logLevel>=0) {
  646. AddMessage(m_pNetwork->GetCurNick(),Nick.GetNickMask(),"ACTION",sMessage);
  647. }
  648. return CONTINUE;
  649. }
  650. virtual EModRet OnChanAction(CNick& Nick, CChan& Channel, CString& sMessage) {
  651. if (logLevel==0) {
  652. if (strcasestr(sMessage.c_str(),m_pNetwork->GetCurNick().c_str()))
  653. AddMessage(Channel.GetName(),Nick.GetNickMask(),"ACTION",sMessage);
  654. } else if (logLevel>=1) {
  655. AddMessage(Channel.GetName(),Nick.GetNickMask(),"ACTION",sMessage);
  656. }
  657. return CONTINUE;
  658. }
  659. virtual EModRet OnPrivMsg(CNick& Nick, CString& sMessage) {
  660. if (logLevel>=0) {
  661. AddMessage(m_pNetwork->GetCurNick(),Nick.GetNickMask(),"PRIVMSG",sMessage);
  662. }
  663. return CONTINUE;
  664. }
  665. virtual EModRet OnChanMsg(CNick& Nick, CChan& Channel, CString& sMessage) {
  666. if (logLevel==0) {
  667. if (strcasestr(sMessage.c_str(),m_pNetwork->GetCurNick().c_str()))
  668. AddMessage(Channel.GetName(),Nick.GetNickMask(),"PRIVMSG",sMessage);
  669. } else if (logLevel>=1) {
  670. AddMessage(Channel.GetName(),Nick.GetNickMask(),"PRIVMSG",sMessage);
  671. }
  672. return CONTINUE;
  673. }
  674. virtual EModRet OnPrivNotice(CNick& Nick, CString& sMessage) {
  675. if (logLevel>=2) {
  676. AddMessage(m_pNetwork->GetCurNick(),Nick.GetNickMask(),"NOTICE",sMessage);
  677. }
  678. return CONTINUE;
  679. }
  680. virtual EModRet OnChanNotice(CNick& Nick, CChan& Channel, CString& sMessage) {
  681. if (logLevel>=2) {
  682. AddMessage(Channel.GetName(),Nick.GetNickMask(),"NOTICE",sMessage);
  683. }
  684. return CONTINUE;
  685. }
  686. virtual EModRet OnTopic(CNick& Nick, CChan& Channel, CString& sTopic) {
  687. if (logLevel>=2) {
  688. AddMessage(Channel.GetName(),Nick.GetNickMask(),"TOPIC",sTopic);
  689. }
  690. return CONTINUE;
  691. }
  692. virtual EModRet OnRaw(CString& sLine) {
  693. if (logLevel>=3) {
  694. CString sCmd = sLine.Token(1);
  695. if ((sCmd.length() == 3) && (isdigit(sCmd[0])) && (isdigit(sCmd[1])) && (isdigit(sCmd[2]))) {
  696. unsigned int uRaw = sCmd.ToUInt();
  697. 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) {
  698. AddMessage("",sLine.Token(0),sCmd,sLine.Token(2, true));
  699. }
  700. }
  701. }
  702. return CONTINUE;
  703. }
  704. //Client Connection
  705. virtual void OnClientLogin() {
  706. SetSetting("clientConnected",GetUNIXTime());
  707. Replay();
  708. }
  709. virtual void OnClientDisconnect() {
  710. if (!m_pNetwork->IsUserAttached()) {
  711. SetSetting("clientDisconnected",GetUNIXTime());
  712. }
  713. }
  714. void Replay() {
  715. PutUser(":*LogSQLite!LogSQLite@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Buffer Playback...");
  716. CString lastOnline = GetSetting("clientDisconnected");
  717. sqlite3_stmt *result;
  718. int status = SQLITE_OK;
  719. if (!replayAll && !lastOnline.empty()) {
  720. status = sqlite3_prepare(database, "SELECT * FROM `messages` WHERE `time`>? ORDER BY `time`", -1, &result, NULL);
  721. if (status!=SQLITE_OK) {
  722. PutUser(":*LogSQLite!LogSQLite@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Playback failed due to sql problem.");
  723. return;
  724. }
  725. status = sqlite3_bind_text(result, 1, lastOnline.c_str(), lastOnline.length(), SQLITE_STATIC);
  726. if (status!=SQLITE_OK) {
  727. PutUser(":*LogSQLite!LogSQLite@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Playback failed due to sql problem.");
  728. sqlite3_finalize(result);
  729. return;
  730. }
  731. } else {
  732. status = sqlite3_prepare(database, "SELECT * FROM `messages` ORDER BY `time`", -1, &result, NULL);
  733. if (status!=SQLITE_OK) {
  734. PutUser(":*LogSQLite!LogSQLite@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Playback failed due to sql problem.");
  735. return;
  736. }
  737. }
  738. int columnCount = sqlite3_column_count(result);
  739. map<int,CString> columns;
  740. for (int i=0; i<columnCount; i++) {
  741. columns[i] = CString(sqlite3_column_name(result, i));
  742. }
  743. while (true) {
  744. status = SQLITE_BUSY;
  745. while (status==SQLITE_BUSY) {
  746. status = sqlite3_step(result);
  747. }
  748. if (status!=SQLITE_ROW)
  749. break;
  750. map<CString,CString> data;
  751. int dataCount = sqlite3_data_count(result);
  752. for (int i=0; i<dataCount; i++) {
  753. data[columns[i]] = CString((const char *)sqlite3_column_text(result, i));
  754. }
  755. time_t now = time(NULL);
  756. time_t unixTime = (time_t)strtol(data["time"].c_str(),NULL,10);
  757. struct tm *timeinfo = localtime(&unixTime);
  758. char timeStr[20];
  759. if (((long)now-86000)<(long)time)
  760. strftime(timeStr, sizeof(timeStr), "%I:%M:%S %p", timeinfo);
  761. else
  762. strftime(timeStr, sizeof(timeStr), "%m/%d/%y %I:%M:%S %p", timeinfo);
  763. if (data["type"].Equals("DISCONNECT")) {
  764. PutUser(":*LogSQLite!LogSQLite@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :["+timeStr+"] Server Disconnected");
  765. } else if (data["type"].Equals("CONNECT")) {
  766. PutUser(":*LogSQLite!LogSQLite@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :["+timeStr+"] Server Connected");
  767. } else if (data["type"].Equals("JOIN")) {
  768. PutUser(":"+data["nick"]+" NOTICE "+data["target"]+" :["+timeStr+"] joined");
  769. } else if (data["type"].Equals("PART")) {
  770. if (data["message"].Equals(""))
  771. PutUser(":"+data["nick"]+" NOTICE "+data["target"]+" :["+timeStr+"] parted");
  772. else
  773. PutUser(":"+data["nick"]+" NOTICE "+data["target"]+" :["+timeStr+"] parted: "+data["message"]);
  774. } else if (data["type"].Equals("TOPIC")) {
  775. PutUser(":"+data["nick"]+" NOTICE "+data["target"]+" :["+timeStr+"] changed topic: "+data["message"]);
  776. } else if (data["type"].Equals("QUIT")) {
  777. if (data["message"].Equals(""))
  778. PutUser(":"+data["nick"]+" NOTICE "+data["target"]+" :["+timeStr+"] quit");
  779. else
  780. PutUser(":"+data["nick"]+" NOTICE "+data["target"]+" :["+timeStr+"] quit: "+data["message"]);
  781. } else if (data["type"].Equals("MODE")) {
  782. PutUser(":"+data["nick"]+" NOTICE "+data["target"]+" :["+timeStr+"] changed mode: "+data["message"]);
  783. } else if (data["type"].Equals("ACTION")) {
  784. PutUser(":"+data["nick"]+" PRIVMSG "+data["target"]+" :\001ACTION ["+timeStr+"] "+data["message"]+"\001");
  785. } else {
  786. PutUser(":"+data["nick"]+" "+data["type"]+" "+data["target"]+" :["+timeStr+"] "+data["message"]);
  787. }
  788. }
  789. sqlite3_finalize(result);
  790. if (logLimit==1) {
  791. status = sqlite3_prepare(database, "DELETE FROM `messages`", -1, &result, NULL);
  792. if (status==SQLITE_OK) {
  793. sqlite3_step(result);
  794. sqlite3_finalize(result);
  795. }
  796. }
  797. PutUser(":*LogSQLite!LogSQLite@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Playback Complete.");
  798. }
  799. private:
  800. sqlite3 *database;
  801. bool connected;
  802. bool replayAll;
  803. unsigned long logLimit;
  804. int logLevel;
  805. vector<CString> nickIgnoreList;
  806. vector<CString> chanIgnoreList;
  807. };
  808. template<> void TModInfo<CLogSQLite>(CModInfo& Info) {
  809. Info.SetWikiPage("logsqlite");
  810. }
  811. NETWORKMODULEDEFS(CLogSQLite, "Add logging to SQLite")