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.

650 lines
25 KiB

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),
  30. "", "Play back the messages received.");
  31. AddCommand("ReplayAll", static_cast<CModCommand::ModCmdFunc>(&CLogSQLite::ReplayAllCommand),
  32. "[1|0]", "Set LogSQLite to replay all messages stored (default is off).");
  33. AddCommand("LogLimit", static_cast<CModCommand::ModCmdFunc>(&CLogSQLite::LogLimitCommand),
  34. "[1|0]", "Set LogSQLite 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).");
  35. AddCommand("LogLevel", static_cast<CModCommand::ModCmdFunc>(&CLogSQLite::LogLevelCommand),
  36. "[0-4]", "Set LogSQLite 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).");
  37. }
  38. void ReplayCommand(const CString &sLine) {
  39. Replay();
  40. PutModule("Replayed");
  41. }
  42. void ReplayAllCommand(const CString &sLine) {
  43. CString sArgs = sLine.Token(1, true);
  44. if (sArgs.empty()) {
  45. CString status = (replayAll ? "On" : "Off");
  46. PutModule("ReplayAll is set to: "+status);
  47. } else {
  48. if (sArgs.Equals("ON") || sArgs.Equals("1") || sArgs.Equals("true")) {
  49. replayAll = true;
  50. SetSetting("replayAll", "1");
  51. } else {
  52. replayAll = false;
  53. SetSetting("replayAll", "0");
  54. }
  55. CString status = (replayAll ? "On" : "Off");
  56. PutModule("ReplayAll is now set to: "+status);
  57. }
  58. }
  59. void LogLimitCommand(const CString &sLine) {
  60. CString sArgs = sLine.Token(1, true);
  61. if (sArgs.empty()) {
  62. CString result;
  63. char limitStr[20];
  64. snprintf(limitStr, sizeof(limitStr), "%lu", logLimit);
  65. result = limitStr;
  66. PutModule("LogLimit is set to: "+result);
  67. } else {
  68. logLimit = strtoul(sArgs.c_str(), NULL, 10);
  69. CString result;
  70. char limitStr[20];
  71. snprintf(limitStr, sizeof(limitStr), "%lu", logLimit);
  72. result = limitStr;
  73. SetSetting("logLimit", result);
  74. PutModule("LogLimit is now set to: "+result);
  75. }
  76. }
  77. void LogLevelCommand(const CString &sLine) {
  78. CString sArgs = sLine.Token(1, true);
  79. if (sArgs.empty()) {
  80. CString result;
  81. char limitStr[20];
  82. snprintf(limitStr, sizeof(limitStr), "%lu", logLimit);
  83. result = limitStr;
  84. PutModule("LogLimit is set to: "+result);
  85. } else {
  86. logLimit = strtoul(sArgs.c_str(), NULL, 10);
  87. CString result;
  88. char limitStr[20];
  89. snprintf(limitStr, sizeof(limitStr), "%lu", logLimit);
  90. result = limitStr;
  91. SetSetting("logLimit", result);
  92. PutModule("LogLimit is now set to: "+result);
  93. }
  94. }
  95. virtual bool OnLoad(const CString& sArgs, CString& sMessage) {
  96. connected = true;
  97. CString savePath = GetSavePath();
  98. savePath += "/log.sqlite";
  99. bool found = false;
  100. FILE *fp = fopen(savePath.c_str(), "rb");
  101. if (fp!=NULL) {
  102. found = true;
  103. fclose(fp);
  104. }
  105. sqlite3_open(savePath.c_str(), &database);
  106. if (!found) {
  107. sqlite3_stmt *result;
  108. int status = sqlite3_prepare(database, "CREATE TABLE `settings` (`name` text, `value` text)", -1, &result, NULL);
  109. if (status==SQLITE_OK) {
  110. sqlite3_step(result);
  111. sqlite3_finalize(result);
  112. }
  113. status = sqlite3_prepare(database, "CREATE TABLE `messages` (`target` text, `nick` text, `type` text, `message` text, `time` real(20,5))", -1, &result, NULL);
  114. if (status==SQLITE_OK) {
  115. sqlite3_step(result);
  116. sqlite3_finalize(result);
  117. }
  118. SetSetting("replayAll","0");
  119. SetSetting("logLimit","1");
  120. SetSetting("logLevel","1");
  121. }
  122. replayAll = atoi(GetSetting("replayAll").c_str());
  123. logLimit = strtoul(GetSetting("logLimit").c_str(), NULL, 10);
  124. logLevel = atoi(GetSetting("logLevel").c_str());
  125. return true;
  126. }
  127. virtual ~CLogSQLite() {
  128. int result = sqlite3_close(database);
  129. if (result==SQLITE_BUSY) {
  130. sqlite3_stmt *stmt;
  131. while ((stmt = sqlite3_next_stmt(database, NULL))!=NULL) {
  132. sqlite3_finalize(stmt);
  133. }
  134. result = sqlite3_close(database);
  135. if (result!=SQLITE_OK)
  136. printf("Unable to close the SQLite Database\n");
  137. }
  138. }
  139. CString GetUNIXTime() {
  140. struct timeval time;
  141. gettimeofday(&time, NULL);
  142. double microtime = (double)(time.tv_sec + (time.tv_usec/1000000.00));
  143. char timeStr[25];
  144. snprintf(timeStr, sizeof(timeStr), "%f", microtime);
  145. return timeStr;
  146. }
  147. void AddMessage(const CString& target, const CString& nick, const CString& type, const CString& message) {
  148. sqlite3_stmt *result;
  149. int status = sqlite3_prepare(database, "INSERT INTO `messages` (`target`, `nick`, `type`, `message`, `time`) VALUES (?,?,?,?,?)", -1, &result, NULL);
  150. if (status!=SQLITE_OK)
  151. return;
  152. status = sqlite3_bind_text(result, 1, target.c_str(), target.length(), SQLITE_STATIC);
  153. if (status!=SQLITE_OK) {
  154. sqlite3_finalize(result);
  155. return;
  156. }
  157. status = sqlite3_bind_text(result, 2, nick.c_str(), nick.length(), SQLITE_STATIC);
  158. if (status!=SQLITE_OK) {
  159. sqlite3_finalize(result);
  160. return;
  161. }
  162. status = sqlite3_bind_text(result, 3, type.c_str(), type.length(), SQLITE_STATIC);
  163. if (status!=SQLITE_OK) {
  164. sqlite3_finalize(result);
  165. return;
  166. }
  167. status = sqlite3_bind_text(result, 4, message.c_str(), message.length(), SQLITE_STATIC);
  168. if (status!=SQLITE_OK) {
  169. sqlite3_finalize(result);
  170. return;
  171. }
  172. CString time = GetUNIXTime();
  173. status = sqlite3_bind_text(result, 5, time.c_str(), time.length(), SQLITE_STATIC);
  174. if (status!=SQLITE_OK) {
  175. sqlite3_finalize(result);
  176. return;
  177. }
  178. sqlite3_step(result);
  179. sqlite3_finalize(result);
  180. if (logLimit>=2) {
  181. status = sqlite3_prepare(database, "SELECT COUNT(*) FROM `messages`", -1, &result, NULL);
  182. if (status!=SQLITE_OK)
  183. return;
  184. status = SQLITE_BUSY;
  185. while (status==SQLITE_BUSY) {
  186. status = sqlite3_step(result);
  187. }
  188. if (status!=SQLITE_ROW)
  189. return;
  190. int dataCount = sqlite3_data_count(result);
  191. if (dataCount!=1) {
  192. sqlite3_finalize(result);
  193. cout << "LogSQLite: We are only suppose to receive 1 field. We received the count of " << dataCount << " fields.\n";
  194. return;
  195. }
  196. unsigned long count = strtoul((const char *)sqlite3_column_text(result, 0), NULL, 10);
  197. if (count<=logLimit)
  198. count = 0;
  199. else
  200. count = count-logLimit;
  201. sqlite3_finalize(result);
  202. if (count!=0) {
  203. char *queryStr = (char *)malloc(73);
  204. snprintf(queryStr, 73, "SELECT `rowid` FROM `messages` ORDER BY `time` LIMIT %lu", count);
  205. status = sqlite3_prepare(database, queryStr, -1, &result, NULL);
  206. free(queryStr);
  207. if (status!=SQLITE_OK)
  208. return;
  209. while (true) {
  210. status = SQLITE_BUSY;
  211. while (status==SQLITE_BUSY) {
  212. status = sqlite3_step(result);
  213. }
  214. if (status!=SQLITE_ROW)
  215. break;
  216. dataCount = sqlite3_data_count(result);
  217. if (dataCount!=1) {
  218. sqlite3_finalize(result);
  219. cout << "LogSQLite: We are only suppose to receive 1 field. We received the count of " << dataCount << " fields.\n";
  220. break;
  221. }
  222. sqlite3_stmt *result2;
  223. const char *rowid = (const char *)sqlite3_column_text(result, 0);
  224. status = sqlite3_prepare(database, "DELETE FROM `messages` WHERE `rowid`=?", -1, &result2, NULL);
  225. if (status!=SQLITE_OK)
  226. continue;
  227. status = sqlite3_bind_text(result2, 1, rowid, strlen(rowid), SQLITE_STATIC);
  228. if (status!=SQLITE_OK) {
  229. sqlite3_finalize(result2);
  230. continue;
  231. }
  232. sqlite3_step(result2);
  233. sqlite3_finalize(result2);
  234. }
  235. sqlite3_finalize(result);
  236. }
  237. }
  238. }
  239. void SetSetting(const CString& name, const CString& value) {
  240. bool exists = false;
  241. sqlite3_stmt *result;
  242. int status = sqlite3_prepare(database, "SELECT `value` FROM `settings` WHERE `name`=?", -1, &result, NULL);
  243. if (status==SQLITE_OK) {
  244. status = sqlite3_bind_text(result, 1, name.c_str(), name.length(), SQLITE_STATIC);
  245. if (status==SQLITE_OK) {
  246. status = SQLITE_BUSY;
  247. while (status==SQLITE_BUSY) {
  248. status = sqlite3_step(result);
  249. }
  250. if (status==SQLITE_ROW) {
  251. exists = true;
  252. }
  253. }
  254. sqlite3_finalize(result);
  255. }
  256. if (exists) {
  257. status = sqlite3_prepare(database, "UPDATE `settings` SET `value`=? WHERE `name`=?", -1, &result, NULL);
  258. if (status!=SQLITE_OK) {
  259. return;
  260. }
  261. status = sqlite3_bind_text(result, 1, value.c_str(), value.length(), SQLITE_STATIC);
  262. if (status!=SQLITE_OK) {
  263. sqlite3_finalize(result);
  264. return;
  265. }
  266. status = sqlite3_bind_text(result, 2, name.c_str(), name.length(), SQLITE_STATIC);
  267. if (status!=SQLITE_OK) {
  268. sqlite3_finalize(result);
  269. return;
  270. }
  271. sqlite3_step(result);
  272. sqlite3_finalize(result);
  273. } else {
  274. status = sqlite3_prepare(database, "INSERT INTO `settings` (`name`,`value`) VALUES (?,?)", -1, &result, NULL);
  275. if (status!=SQLITE_OK) {
  276. return;
  277. }
  278. status = sqlite3_bind_text(result, 1, name.c_str(), name.length(), SQLITE_STATIC);
  279. if (status!=SQLITE_OK) {
  280. sqlite3_finalize(result);
  281. return;
  282. }
  283. status = sqlite3_bind_text(result, 2, value.c_str(), value.length(), SQLITE_STATIC);
  284. if (status!=SQLITE_OK) {
  285. sqlite3_finalize(result);
  286. return;
  287. }
  288. sqlite3_step(result);
  289. sqlite3_finalize(result);
  290. }
  291. }
  292. CString GetSetting(const CString& name) {
  293. CString stringValue;
  294. sqlite3_stmt *result;
  295. int status = sqlite3_prepare(database, "SELECT `value` FROM `settings` WHERE `name`=?", -1, &result, NULL);
  296. if (status!=SQLITE_OK)
  297. return stringValue;
  298. status = sqlite3_bind_text(result, 1, name.c_str(), name.length(), SQLITE_STATIC);
  299. if (status!=SQLITE_OK) {
  300. sqlite3_finalize(result);
  301. return stringValue;
  302. }
  303. while (true) {
  304. status = SQLITE_BUSY;
  305. while (status==SQLITE_BUSY) {
  306. status = sqlite3_step(result);
  307. }
  308. if (status!=SQLITE_ROW)
  309. break;
  310. int dataCount = sqlite3_data_count(result);
  311. if (dataCount!=1) {
  312. sqlite3_finalize(result);
  313. cout << "LogSQLite: Settings are only suppose to return 1 as the field count. We received the count of " << dataCount << " fields.\n";
  314. return stringValue;
  315. }
  316. stringValue = CString((const char *)sqlite3_column_text(result, 0));
  317. break;
  318. }
  319. sqlite3_finalize(result);
  320. return stringValue;
  321. }
  322. //Server stuff
  323. virtual void OnIRCDisconnected() {
  324. if (connected) {
  325. connected = false;
  326. if (logLevel>=3) {
  327. AddMessage("","","DISCONNECT","");
  328. }
  329. }
  330. }
  331. virtual void OnIRCConnected() {
  332. if (!connected) {
  333. connected = true;
  334. if (logLevel>=3) {
  335. AddMessage("","","CONNECT","");
  336. }
  337. }
  338. }
  339. //User stuff
  340. virtual EModRet OnUserAction(CString& sTarget, CString& sMessage) {
  341. if (logLevel>=4) {
  342. AddMessage(sTarget,m_pNetwork->GetIRCNick().GetNickMask(),"ACTION",sMessage);
  343. }
  344. return CONTINUE;
  345. }
  346. virtual EModRet OnUserMsg(CString& sTarget, CString& sMessage) {
  347. if (logLevel>=4) {
  348. AddMessage(sTarget,m_pNetwork->GetIRCNick().GetNickMask(),"PRIVMSG",sMessage);
  349. }
  350. return CONTINUE;
  351. }
  352. virtual EModRet OnUserNotice(CString& sTarget, CString& sMessage) {
  353. if (logLevel>=4) {
  354. AddMessage(sTarget,m_pNetwork->GetIRCNick().GetNickMask(),"NOTICE",sMessage);
  355. }
  356. return CONTINUE;
  357. }
  358. virtual EModRet OnUserJoin(CString& sChannel, CString& sKey) {
  359. if (logLevel>=4) {
  360. AddMessage(sChannel,m_pNetwork->GetIRCNick().GetNickMask(),"JOIN","");
  361. }
  362. return CONTINUE;
  363. }
  364. virtual EModRet OnUserPart(CString& sChannel, CString& sMessage) {
  365. if (logLevel>=4) {
  366. AddMessage(sChannel,m_pNetwork->GetIRCNick().GetNickMask(),"PART",sMessage);
  367. }
  368. return CONTINUE;
  369. }
  370. virtual EModRet OnUserTopic(CString& sChannel, CString& sTopic) {
  371. if (logLevel>=4) {
  372. AddMessage(sChannel,m_pNetwork->GetIRCNick().GetNickMask(),"TOPIC",sTopic);
  373. }
  374. return CONTINUE;
  375. }
  376. //Other stuff
  377. virtual void OnRawMode(const CNick& OpNick, CChan& Channel, const CString& sModes, const CString& sArgs) {
  378. if (logLevel>=3) {
  379. AddMessage(Channel.GetName(),OpNick.GetNickMask(),"MODE",sModes+" "+sArgs);
  380. }
  381. }
  382. virtual void OnKick(const CNick& OpNick, const CString& sKickedNick, CChan& Channel, const CString& sMessage) {
  383. if (logLevel>=2) {
  384. AddMessage(Channel.GetName(),OpNick.GetNickMask(),"KICK",sKickedNick+" "+sMessage);
  385. }
  386. }
  387. virtual void OnQuit(const CNick& Nick, const CString& sMessage, const vector<CChan*>& vChans) {
  388. if (logLevel>=2) {
  389. vector<CChan*>::const_iterator it;
  390. for (it=vChans.begin(); it!=vChans.end(); it++) {
  391. CChan& channel = **it;
  392. AddMessage(channel.GetName(),Nick.GetNickMask(),"QUIT",sMessage);
  393. }
  394. }
  395. }
  396. virtual void OnJoin(const CNick& Nick, CChan& Channel) {
  397. if (logLevel>=2) {
  398. AddMessage(Channel.GetName(),Nick.GetNickMask(),"JOIN","");
  399. }
  400. }
  401. virtual void OnPart(const CNick& Nick, CChan& Channel, const CString& sMessage) {
  402. if (logLevel>=2) {
  403. AddMessage(Channel.GetName(),Nick.GetNickMask(),"JOIN",sMessage);
  404. }
  405. }
  406. virtual void OnNick(const CNick& OldNick, const CString& sNewNick, const vector<CChan*>& vChans) {
  407. if (logLevel>=2) {
  408. vector<CChan*>::const_iterator it;
  409. for (it=vChans.begin(); it!=vChans.end(); it++) {
  410. CChan& channel = **it;
  411. AddMessage(channel.GetName(),OldNick.GetNickMask(),"NICK",sNewNick);
  412. }
  413. }
  414. }
  415. virtual EModRet OnPrivAction(CNick& Nick, CString& sMessage) {
  416. if (logLevel>=0) {
  417. AddMessage(m_pNetwork->GetCurNick(),Nick.GetNickMask(),"ACTION",sMessage);
  418. }
  419. return CONTINUE;
  420. }
  421. virtual EModRet OnChanAction(CNick& Nick, CChan& Channel, CString& sMessage) {
  422. if (logLevel==0) {
  423. if (strcasestr(sMessage.c_str(),m_pNetwork->GetCurNick().c_str()))
  424. AddMessage(Channel.GetName(),Nick.GetNickMask(),"ACTION",sMessage);
  425. } else if (logLevel>=1) {
  426. AddMessage(Channel.GetName(),Nick.GetNickMask(),"ACTION",sMessage);
  427. }
  428. return CONTINUE;
  429. }
  430. virtual EModRet OnPrivMsg(CNick& Nick, CString& sMessage) {
  431. if (logLevel>=0) {
  432. AddMessage(m_pNetwork->GetCurNick(),Nick.GetNickMask(),"PRIVMSG",sMessage);
  433. }
  434. return CONTINUE;
  435. }
  436. virtual EModRet OnChanMsg(CNick& Nick, CChan& Channel, CString& sMessage) {
  437. if (logLevel==0) {
  438. if (strcasestr(sMessage.c_str(),m_pNetwork->GetCurNick().c_str()))
  439. AddMessage(Channel.GetName(),Nick.GetNickMask(),"PRIVMSG",sMessage);
  440. } else if (logLevel>=1) {
  441. AddMessage(Channel.GetName(),Nick.GetNickMask(),"PRIVMSG",sMessage);
  442. }
  443. return CONTINUE;
  444. }
  445. virtual EModRet OnPrivNotice(CNick& Nick, CString& sMessage) {
  446. if (logLevel>=2) {
  447. AddMessage(m_pNetwork->GetCurNick(),Nick.GetNickMask(),"NOTICE",sMessage);
  448. }
  449. return CONTINUE;
  450. }
  451. virtual EModRet OnChanNotice(CNick& Nick, CChan& Channel, CString& sMessage) {
  452. if (logLevel>=2) {
  453. AddMessage(Channel.GetName(),Nick.GetNickMask(),"NOTICE",sMessage);
  454. }
  455. return CONTINUE;
  456. }
  457. virtual EModRet OnTopic(CNick& Nick, CChan& Channel, CString& sTopic) {
  458. if (logLevel>=2) {
  459. AddMessage(Channel.GetName(),Nick.GetNickMask(),"TOPIC",sTopic);
  460. }
  461. return CONTINUE;
  462. }
  463. virtual EModRet OnRaw(CString& sLine) {
  464. if (logLevel>=3) {
  465. CString sCmd = sLine.Token(1);
  466. if ((sCmd.length() == 3) && (isdigit(sCmd[0])) && (isdigit(sCmd[1])) && (isdigit(sCmd[2]))) {
  467. unsigned int uRaw = sCmd.ToUInt();
  468. 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) {
  469. AddMessage("",sLine.Token(0),sCmd,sLine.Token(2, true));
  470. }
  471. }
  472. }
  473. return CONTINUE;
  474. }
  475. //Client Connection
  476. virtual void OnClientLogin() {
  477. SetSetting("clientConnected",GetUNIXTime());
  478. Replay();
  479. }
  480. virtual void OnClientDisconnect() {
  481. if (!m_pNetwork->IsUserAttached()) {
  482. SetSetting("clientDisconnected",GetUNIXTime());
  483. }
  484. }
  485. void Replay() {
  486. PutUser(":*LogSQLite!LogSQLite@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Buffer Playback...");
  487. CString lastOnline = GetSetting("clientDisconnected");
  488. sqlite3_stmt *result;
  489. int status = SQLITE_OK;
  490. if (!replayAll && !lastOnline.empty()) {
  491. status = sqlite3_prepare(database, "SELECT * FROM `messages` WHERE `time`>? ORDER BY `time`", -1, &result, NULL);
  492. if (status!=SQLITE_OK) {
  493. PutUser(":*LogSQLite!LogSQLite@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Playback failed due to sql problem.");
  494. return;
  495. }
  496. status = sqlite3_bind_text(result, 1, lastOnline.c_str(), lastOnline.length(), SQLITE_STATIC);
  497. if (status!=SQLITE_OK) {
  498. PutUser(":*LogSQLite!LogSQLite@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Playback failed due to sql problem.");
  499. sqlite3_finalize(result);
  500. return;
  501. }
  502. } else {
  503. status = sqlite3_prepare(database, "SELECT * FROM `messages` ORDER BY `time`", -1, &result, NULL);
  504. if (status!=SQLITE_OK) {
  505. PutUser(":*LogSQLite!LogSQLite@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Playback failed due to sql problem.");
  506. return;
  507. }
  508. }
  509. int columnCount = sqlite3_column_count(result);
  510. map<int,CString> columns;
  511. for (int i=0; i<columnCount; i++) {
  512. columns[i] = CString(sqlite3_column_name(result, i));
  513. }
  514. while (true) {
  515. status = SQLITE_BUSY;
  516. while (status==SQLITE_BUSY) {
  517. status = sqlite3_step(result);
  518. }
  519. if (status!=SQLITE_ROW)
  520. break;
  521. map<CString,CString> data;
  522. int dataCount = sqlite3_data_count(result);
  523. for (int i=0; i<dataCount; i++) {
  524. data[columns[i]] = CString((const char *)sqlite3_column_text(result, i));
  525. }
  526. time_t now = time(NULL);
  527. time_t unixTime = (time_t)strtol(data["time"].c_str(),NULL,10);
  528. struct tm *timeinfo = localtime(&unixTime);
  529. char timeStr[20];
  530. if (((long)now-86000)<(long)time)
  531. strftime(timeStr, sizeof(timeStr), "%I:%M:%S %p", timeinfo);
  532. else
  533. strftime(timeStr, sizeof(timeStr), "%m/%d/%y %I:%M:%S %p", timeinfo);
  534. if (data["type"].Equals("DISCONNECT")) {
  535. PutUser(":*LogSQLite!LogSQLite@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :["+timeStr+"] Server Disconnected");
  536. } else if (data["type"].Equals("CONNECT")) {
  537. PutUser(":*LogSQLite!LogSQLite@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :["+timeStr+"] Server Connected");
  538. } else if (data["type"].Equals("JOIN")) {
  539. PutUser(":"+data["nick"]+" NOTICE "+data["target"]+" :["+timeStr+"] joined");
  540. } else if (data["type"].Equals("PART")) {
  541. if (data["message"].Equals(""))
  542. PutUser(":"+data["nick"]+" NOTICE "+data["target"]+" :["+timeStr+"] parted");
  543. else
  544. PutUser(":"+data["nick"]+" NOTICE "+data["target"]+" :["+timeStr+"] parted: "+data["message"]);
  545. } else if (data["type"].Equals("TOPIC")) {
  546. PutUser(":"+data["nick"]+" NOTICE "+data["target"]+" :["+timeStr+"] changed topic: "+data["message"]);
  547. } else if (data["type"].Equals("QUIT")) {
  548. if (data["message"].Equals(""))
  549. PutUser(":"+data["nick"]+" NOTICE "+data["target"]+" :["+timeStr+"] quit");
  550. else
  551. PutUser(":"+data["nick"]+" NOTICE "+data["target"]+" :["+timeStr+"] quit: "+data["message"]);
  552. } else if (data["type"].Equals("MODE")) {
  553. PutUser(":"+data["nick"]+" NOTICE "+data["target"]+" :["+timeStr+"] changed mode: "+data["message"]);
  554. } else if (data["type"].Equals("ACTION")) {
  555. PutUser(":"+data["nick"]+" PRIVMSG "+data["target"]+" :\001ACTION ["+timeStr+"] "+data["message"]+"\001");
  556. } else {
  557. PutUser(":"+data["nick"]+" "+data["type"]+" "+data["target"]+" :["+timeStr+"] "+data["message"]);
  558. }
  559. }
  560. sqlite3_finalize(result);
  561. if (logLimit==1) {
  562. status = sqlite3_prepare(database, "DELETE FROM `messages`", -1, &result, NULL);
  563. if (status==SQLITE_OK) {
  564. sqlite3_step(result);
  565. sqlite3_finalize(result);
  566. }
  567. }
  568. PutUser(":*LogSQLite!LogSQLite@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Playback Complete.");
  569. }
  570. private:
  571. sqlite3 *database;
  572. bool connected;
  573. bool replayAll;
  574. unsigned long logLimit;
  575. int logLevel;
  576. };
  577. template<> void TModInfo<CLogSQLite>(CModInfo& Info) {
  578. Info.SetWikiPage("logsqlite");
  579. }
  580. NETWORKMODULEDEFS(CLogSQLite, "Add logging to SQLite")