From cd4ceb6f47d722a5bdbaa13386fd72fce494de1c Mon Sep 17 00:00:00 2001 From: GRMrGecko Date: Thu, 12 Mar 2015 13:48:01 -0500 Subject: [PATCH] Added new LogSQLite which keeps a side database containing the last time the server has seen an individual. Added new AutoReplay setting to disable the auto replay on connect. Made the replay command have the ability to be called with the amount of items max you want to have replayed. --- logmysql.cpp | 184 ++++--- logsqlite-seen.cpp | 1189 ++++++++++++++++++++++++++++++++++++++++++++ logsqlite.cpp | 122 +++-- 3 files changed, 1396 insertions(+), 99 deletions(-) create mode 100755 logsqlite-seen.cpp diff --git a/logmysql.cpp b/logmysql.cpp index 720d5d8..70b568d 100755 --- a/logmysql.cpp +++ b/logmysql.cpp @@ -37,8 +37,9 @@ public: AddCommand("DatabaseName", static_cast(&CLogMySQL::DatabaseNameCommand), "[a-z0-9]+", "MySQL database name."); AddCommand("Connect", static_cast(&CLogMySQL::ConnectCommand), "", "Reconnect to the MySQL database."); - AddCommand("Replay", static_cast(&CLogMySQL::ReplayCommand), "", "Play back the messages received."); + AddCommand("Replay", static_cast(&CLogMySQL::ReplayCommand), "[0-9]*", "Play back the messages received."); AddCommand("ReplayAll", static_cast(&CLogMySQL::ReplayAllCommand), "[1|0]", "Replay all messages stored."); + AddCommand("AutoReplay", static_cast(&CLogMySQL::AutoReplayCommand), "[1|0]", "Replay on connect."); AddCommand("LogLimit", static_cast(&CLogMySQL::LogLimitCommand), "[0-9]+", "Limit the amount of items to store into the log."); AddCommand("LogLevel", static_cast(&CLogMySQL::LogLevelCommand), "[0-4]", "Log level."); @@ -129,11 +130,16 @@ public: } - void ReplayCommand(const CString &sLine) { - Replay(); - PutModule("Replayed"); - } - + void ReplayCommand(const CString &sLine) { + CString sArgs = sLine.Token(1, true); + if (!sArgs.empty()) { + Replay(sArgs.ToInt()); + } else { + Replay(); + } + PutModule("Replayed"); + } + void ReplayAllCommand(const CString &sLine) { CString sArgs = sLine.Token(1, true); bool help = sArgs.Equals("HELP"); @@ -149,8 +155,25 @@ public: CString status = (replayAll ? "On" : "Off"); CString now = (sArgs.empty() || help ? "" : "now "); PutModule("ReplayAll is "+now+"set to: "+status); - } - + } + + void AutoReplayCommand(const CString &sLine) { + CString sArgs = sLine.Token(1, true); + bool help = sArgs.Equals("HELP"); + + if (help) { + PutModule("On: Replay on connect."); + PutModule("Off: Require replay command to replay."); + } else if (!sArgs.empty()) { + autoReplay = sArgs.ToBool(); + SetSetting("autoReplay", (autoReplay ? "1" : "0")); + } + + CString status = (autoReplay ? "On" : "Off"); + CString now = (sArgs.empty() || help ? "" : "now "); + PutModule("AutoReplay is "+now+"set to: "+status); + } + void LogLimitCommand(const CString &sLine) { CString sArgs = sLine.Token(1, true); bool help = sArgs.Equals("HELP"); @@ -269,7 +292,7 @@ public: } else { PutModule("Nick ignore list contains:"); - for (vector::iterator it=nickIgnoreList.begin(); it::iterator it=nickIgnoreList.begin(); it::iterator it=chanIgnoreList.begin(); it::iterator it=chanIgnoreList.begin(); it::iterator it=doNotTrackClient.begin(); it::iterator it=doNotTrackClient.begin(); it::iterator it=nickIgnoreList.begin(); it::iterator it=nickIgnoreList.begin(); it::iterator it=chanIgnoreList.begin(); it::iterator it=chanIgnoreList.begin(); it::iterator it=nickIgnoreList.begin(); it::iterator it=nickIgnoreList.begin(); it::iterator it=chanIgnoreList.begin(); it::iterator it=chanIgnoreList.begin(); it& vChans) { + virtual void OnQuit(const CNick& Nick, const CString& sMessage, const std::vector& vChans) { if (logLevel>=2) { - vector::const_iterator it; + std::vector::const_iterator it; for (it=vChans.begin(); it!=vChans.end(); it++) { CChan& channel = **it; AddMessage(channel.GetName(),Nick.GetNickMask(),"QUIT",sMessage); @@ -997,13 +1030,13 @@ public: virtual void OnPart(const CNick& Nick, CChan& Channel, const CString& sMessage) { if (logLevel>=2) { - AddMessage(Channel.GetName(),Nick.GetNickMask(),"JOIN",sMessage); + AddMessage(Channel.GetName(),Nick.GetNickMask(),"PART",sMessage); } } - virtual void OnNick(const CNick& OldNick, const CString& sNewNick, const vector& vChans) { + virtual void OnNick(const CNick& OldNick, const CString& sNewNick, const std::vector& vChans) { if (logLevel>=2) { - vector::const_iterator it; + std::vector::const_iterator it; for (it=vChans.begin(); it!=vChans.end(); it++) { CChan& channel = **it; AddMessage(channel.GetName(),OldNick.GetNickMask(),"NICK",sNewNick); @@ -1082,12 +1115,13 @@ public: //Client Connection virtual void OnClientLogin() { SetSetting("clientConnected",GetUNIXTime()); - Replay(); + if (autoReplay) + Replay(); } virtual void OnClientDisconnect() { bool track = true; - for (vector::iterator it=doNotTrackClient.begin(); it::iterator it=doNotTrackClient.begin(); itGetIRCNick().GetNickMask()+" :Buffer Playback..."); @@ -1107,7 +1145,28 @@ public: MYSQL_STMT *statement = mysql_stmt_init(database); int status = 0; - if (!replayAll && !lastOnline.empty()) { + if (replayCount!=0) { + status = mysql_stmt_prepare(statement, "SELECT * FROM (SELECT * FROM `messages` ORDER BY `time` DESC LIMIT ?) ORDER BY `time`", 85); + if (status!=0) { + PutUser(":*LogMySQL!LogMySQL@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Playback failed due to sql problem."); + return; + } + + MYSQL_BIND bind[1]; + memset(bind, 0, sizeof(bind)); + + bind[0].buffer_type = MYSQL_TYPE_LONG; + bind[0].buffer = (void*)&replayCount; + bind[0].buffer_length = 0; + bind[0].is_null = (my_bool *)0; + + status = mysql_stmt_bind_param(statement, bind); + if (status!=0) { + PutUser(":*LogMySQL!LogMySQL@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Playback failed due to sql problem."); + mysql_stmt_close(statement); + return; + } + } else if (!replayAll && !lastOnline.empty()) { status = mysql_stmt_prepare(statement, "SELECT * FROM `messages` WHERE `time`>? ORDER BY `time`", 55); if (status!=0) { PutUser(":*LogMySQL!LogMySQL@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Playback failed due to sql problem."); @@ -1120,7 +1179,7 @@ public: bind[0].buffer_type = MYSQL_TYPE_STRING; bind[0].buffer = (void*)lastOnline.c_str(); bind[0].buffer_length = lastOnline.length(); - bind[0].is_null = false; + bind[0].is_null = (my_bool *)0; status = mysql_stmt_bind_param(statement, bind); if (status!=0) { @@ -1149,10 +1208,10 @@ public: MYSQL_FIELD *fields = mysql_fetch_fields(result); MYSQL_BIND results[columnCount]; memset(results, 0, sizeof(results)); - map columnData; - map columnLength; - map columnNull; - map columns; + std::map columnData; + std::map columnLength; + std::map columnNull; + std::map columns; for (unsigned int i=0; i data; + std::map data; for (unsigned int i=0; i nickIgnoreList; - vector chanIgnoreList; + std::vector nickIgnoreList; + std::vector chanIgnoreList; - vector doNotTrackClient; + std::vector doNotTrackClient; }; template<> void TModInfo(CModInfo& Info) { diff --git a/logsqlite-seen.cpp b/logsqlite-seen.cpp new file mode 100755 index 0000000..9e6e5b7 --- /dev/null +++ b/logsqlite-seen.cpp @@ -0,0 +1,1189 @@ +// +// logsqlite.cpp +// LogSQL +// +// Created by Mr. Gecko on 2/3/12. +// Copyright (c) 2012 Mr. Gecko's Media (James Coleman). http://mrgeckosmedia.com/ +// +// Permission to use, copy, modify, and/or distribute this software for any purpose +// with or without fee is hereby granted, provided that the above copyright notice +// and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, +// OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +// DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +// ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +// + +#include +#include +#include +#include +#include +#include + +#include + +class CLogSQLite : public CModule { +public: + MODCONSTRUCTOR(CLogSQLite) { + AddHelpCommand(); + AddCommand("Replay", static_cast(&CLogSQLite::ReplayCommand), "[0-9]*", "Play back messages received since last recorded disconnect or to count."); + AddCommand("ReplayAll", static_cast(&CLogSQLite::ReplayAllCommand), "[1|0]", "Replay all messages stored."); + AddCommand("AutoReplay", static_cast(&CLogSQLite::AutoReplayCommand), "[1|0]", "Replay on connect."); + AddCommand("LogLimit", static_cast(&CLogSQLite::LogLimitCommand), "[0-9]+", "Limit the amount of items to store into the log."); + AddCommand("LogLevel", static_cast(&CLogSQLite::LogLevelCommand), "[0-4]", "Log level."); + + AddCommand("AddIgnore", static_cast(&CLogSQLite::AddIgnoreCommand), "Type[nick|chan] Target", "Add to ignore list."); + AddCommand("RemoveIgnore", static_cast(&CLogSQLite::RemoveIgnoreCommand), "Type[nick|chan] Target", "Remove from ignore list."); + AddCommand("IgnoreList", static_cast(&CLogSQLite::IgnoreListCommand), "", "View what is currently ignored."); + + AddCommand("DoNotTrack", static_cast(&CLogSQLite::DoNotTrack), "", "Adds the current session to the do not track disconnect list."); + } + + void ReplayCommand(const CString &sLine) { + CString sArgs = sLine.Token(1, true); + if (!sArgs.empty()) { + Replay(sArgs.ToInt()); + } else { + Replay(); + } + PutModule("Replayed"); + } + + void ReplayAllCommand(const CString &sLine) { + CString sArgs = sLine.Token(1, true); + bool help = sArgs.Equals("HELP"); + + if (help) { + PutModule("On: All logs stored will be replayed."); + PutModule("Off: Only logs since the last time you connected will be replayed."); + } else if (!sArgs.empty()) { + replayAll = sArgs.ToBool(); + SetSetting("replayAll", (replayAll ? "1" : "0")); + } + + CString status = (replayAll ? "On" : "Off"); + CString now = (sArgs.empty() || help ? "" : "now "); + PutModule("ReplayAll is "+now+"set to: "+=status); + } + + void AutoReplayCommand(const CString &sLine) { + CString sArgs = sLine.Token(1, true); + bool help = sArgs.Equals("HELP"); + + if (help) { + PutModule("On: Replay on connect."); + PutModule("Off: Require replay command to replay."); + } else if (!sArgs.empty()) { + autoReplay = sArgs.ToBool(); + SetSetting("autoReplay", (autoReplay ? "1" : "0")); + } + + CString status = (autoReplay ? "On" : "Off"); + CString now = (sArgs.empty() || help ? "" : "now "); + PutModule("AutoReplay is "+now+"set to: "+status); + } + + void LogLimitCommand(const CString &sLine) { + CString sArgs = sLine.Token(1, true); + bool help = sArgs.Equals("HELP"); + + CString setting; + if (help) { + PutModule("0: Everything will be kept in database."); + PutModule("1: Everything will be kept in database until replayed."); + PutModule("2+: Limit logs stored in database to limit set."); + } else if (!sArgs.empty()) { + logLimit = strtoul(sArgs.c_str(), NULL, 10); + char limitStr[20]; + snprintf(limitStr, sizeof(limitStr), "%lu", logLimit); + setting = limitStr; + SetSetting("logLimit", setting); + } + if (setting.empty()) { + char limitStr[20]; + snprintf(limitStr, sizeof(limitStr), "%lu", logLimit); + setting = limitStr; + } + CString now = (sArgs.empty() || help ? "" : "now "); + PutModule("LogLimit is "+now+"set to: "+setting); + } + + void LogLevelCommand(const CString &sLine) { + CString sArgs = sLine.Token(1, true); + bool help = sArgs.Equals("HELP"); + + CString setting; + if (help) { + PutModule("0: Mentions and messages to you."); + PutModule("1: All messages."); + PutModule("2: Actions, Joins/Parts, and Notices."); + PutModule("3: Server wide messages."); + PutModule("4: All messages, actions, joins/parts, and noticies sent by you."); + } else if (!sArgs.empty()) { + logLevel = atoi(sArgs.c_str()); + char levelStr[20]; + snprintf(levelStr, sizeof(levelStr), "%d", logLevel); + setting = levelStr; + SetSetting("logLevel", setting); + } + if (setting.empty()) { + char levelStr[20]; + snprintf(levelStr, sizeof(levelStr), "%d", logLevel); + setting = levelStr; + } + CString now = (sArgs.empty() || help ? "" : "now "); + PutModule("LogLevel is "+now+"set to: "+setting); + } + + void AddIgnoreCommand(const CString &sLine) { + CString type = sLine.Token(1); + CString target = sLine.Token(2); + bool help = sLine.Equals("HELP"); + + if (help) { + PutModule("Inorder to add an ignore, you must choose what type of ignore it is which can ether be a nick or a chan."); + PutModule("Nicks are matched with wildcards against the full mask."); + PutModule("Channels are matched by #channel which can contain wildcards."); + } else if (!type.empty() && !target.empty()) { + if (type.Equals("nick")) + type = "nick"; + else if (type.Equals("chan") || type.Equals("channel")) + type = "chan"; + else + type = ""; + + if (type.empty()) { + PutModule("Unknown type. If you need help, type \"AddIgnore help\"."); + return; + } + + if (AddIgnore(type, target)) + PutModule("Successfully added \""+target+"\" to the ignore list."); + else + PutModule("Failed, maybe it already existed?"); + } else { + PutModule("If you need help, type \"AddIgnore help\"."); + } + } + + void RemoveIgnoreCommand(const CString &sLine) { + CString type = sLine.Token(1); + CString target = sLine.Token(2); + bool help = sLine.Equals("HELP"); + + if (help) { + 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\"."); + } else if (!type.empty() && !target.empty()) { + if (type.Equals("nick")) + type = "nick"; + else if (type.Equals("chan") || type.Equals("channel")) + type = "chan"; + else + type = ""; + + if (type.empty()) { + PutModule("Unknown type. If you need help, type \"RemoveIgnore help\"."); + return; + } + + if (RemoveIgnore(type, target)) + PutModule("Successfully removed \""+target+"\" from the ignore list."); + else + PutModule("Failed, maybe it does not exist?"); + } else { + PutModule("If you need help, type \"RemoveIgnore help\"."); + } + } + + void IgnoreListCommand(const CString &sLine) { + if (nickIgnoreList.size()==0) { + PutModule("The nick ignore list is currently empty."); + } else { + PutModule("Nick ignore list contains:"); + + for (std::vector::iterator it=nickIgnoreList.begin(); it::iterator it=chanIgnoreList.begin(); it::iterator it=doNotTrackClient.begin(); itGetIRCNick().GetNickMask()+" :Added session to do not track list."); + } else { + PutUser(":*LogSQLite!LogSQLite@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Already added session to do not track list."); + } + } + + virtual bool OnLoad(const CString& sArgs, CString& sMessage) { + connected = true; + + CString savePath = GetSavePath(); + savePath += "/log.sqlite"; + + sqlite3_open(savePath.c_str(), &database); + + sqlite3_stmt *result; + int status = sqlite3_prepare(database, "SELECT `name` FROM `sqlite_master` WHERE `name`='settings'", -1, &result, NULL); + if (status==SQLITE_OK) { + status = SQLITE_BUSY; + while (status==SQLITE_BUSY) { + status = sqlite3_step(result); + } + sqlite3_finalize(result); + if (status!=SQLITE_ROW) { + status = sqlite3_prepare(database, "CREATE TABLE `settings` (`name` text, `value` text)", -1, &result, NULL); + if (status==SQLITE_OK) { + sqlite3_step(result); + sqlite3_finalize(result); + + SetSetting("replayAll","0"); + SetSetting("autoReplay","1"); + SetSetting("logLimit","1"); + SetSetting("logLevel","1"); + SetSetting("version","3"); + } + } + } + status = sqlite3_prepare(database, "SELECT `name` FROM `sqlite_master` WHERE `name`='messages'", -1, &result, NULL); + if (status==SQLITE_OK) { + status = SQLITE_BUSY; + while (status==SQLITE_BUSY) { + status = sqlite3_step(result); + } + sqlite3_finalize(result); + if (status!=SQLITE_ROW) { + status = sqlite3_prepare(database, "CREATE TABLE `messages` (`target` text, `nick` text, `type` text, `message` text, `time` real(20,5) PRIMARY KEY)", -1, &result, NULL); + if (status==SQLITE_OK) { + sqlite3_step(result); + sqlite3_finalize(result); + } + } + } + status = sqlite3_prepare(database, "SELECT `name` FROM `sqlite_master` WHERE `name`='ignorelist'", -1, &result, NULL); + if (status==SQLITE_OK) { + status = SQLITE_BUSY; + while (status==SQLITE_BUSY) { + status = sqlite3_step(result); + } + sqlite3_finalize(result); + if (status!=SQLITE_ROW) { + status = sqlite3_prepare(database, "CREATE TABLE `ignorelist` (`type` text, `target` text)", -1, &result, NULL); + if (status==SQLITE_OK) { + sqlite3_step(result); + sqlite3_finalize(result); + } + } + } + + unsigned long version = strtoul(GetSetting("version").c_str(), NULL, 10); + if (version==0) { + SetSetting("version","1"); + version = 1; + } + if (version==1) { + status = sqlite3_prepare(database, "BEGIN TRANSACTION", -1, &result, NULL); + if (status==SQLITE_OK) { + sqlite3_step(result); + sqlite3_finalize(result); + } + status = sqlite3_prepare(database, "CREATE TEMPORARY TABLE `messages_backup` (`target` text, `nick` text, `type` text, `message` text, `time` real(20,5))", -1, &result, NULL); + if (status==SQLITE_OK) { + sqlite3_step(result); + sqlite3_finalize(result); + } + status = sqlite3_prepare(database, "INSERT INTO `messages_backup` SELECT * FROM `messages`", -1, &result, NULL); + if (status==SQLITE_OK) { + sqlite3_step(result); + sqlite3_finalize(result); + } + status = sqlite3_prepare(database, "DROP TABLE `messages`", -1, &result, NULL); + if (status==SQLITE_OK) { + sqlite3_step(result); + sqlite3_finalize(result); + } + status = sqlite3_prepare(database, "CREATE TABLE `messages` (`target` text, `nick` text, `type` text, `message` text, `time` real(20,5) PRIMARY KEY)", -1, &result, NULL); + if (status==SQLITE_OK) { + sqlite3_step(result); + sqlite3_finalize(result); + } + status = sqlite3_prepare(database, "INSERT INTO `messages` SELECT * FROM `messages_backup`", -1, &result, NULL); + if (status==SQLITE_OK) { + sqlite3_step(result); + sqlite3_finalize(result); + } + status = sqlite3_prepare(database, "DROP TABLE `messages_backup`", -1, &result, NULL); + if (status==SQLITE_OK) { + sqlite3_step(result); + sqlite3_finalize(result); + } + status = sqlite3_prepare(database, "COMMIT", -1, &result, NULL); + if (status==SQLITE_OK) { + sqlite3_step(result); + sqlite3_finalize(result); + } + SetSetting("version","2"); + version = 2; + } + if (version==2) { + SetSetting("autoReplay","1"); + SetSetting("version","3"); + version = 3; + } + + replayAll = atoi(GetSetting("replayAll").c_str()); + logLimit = strtoul(GetSetting("logLimit").c_str(), NULL, 10); + logLevel = atoi(GetSetting("logLevel").c_str()); + + UpdateIgnoreLists(); + + + CString seenSavePath = GetSavePath(); + seenSavePath += "/seenLog.sqlite"; + + sqlite3_open(seenSavePath.c_str(), &seenDatabase); + + status = sqlite3_prepare(seenDatabase, "SELECT `name` FROM `sqlite_master` WHERE `name`='log'", -1, &result, NULL); + if (status==SQLITE_OK) { + status = SQLITE_BUSY; + while (status==SQLITE_BUSY) { + status = sqlite3_step(result); + } + sqlite3_finalize(result); + if (status!=SQLITE_ROW) { + status = sqlite3_prepare(seenDatabase, "CREATE TABLE `log` (`nick` text NOT NULL,`lastAction` real(20,5) NOT NULL,`action` text NOT NULL,`lastJoin` real(20,5) NOT NULL,`lastPart` real(20,5) NOT NULL,PRIMARY KEY(`nick`))", -1, &result, NULL); + if (status==SQLITE_OK) { + sqlite3_step(result); + sqlite3_finalize(result); + } + } + } + + return true; + } + + virtual ~CLogSQLite() { + int result = sqlite3_close(database); + if (result==SQLITE_BUSY) { + sqlite3_stmt *stmt; + while ((stmt = sqlite3_next_stmt(database, NULL))!=NULL) { + sqlite3_finalize(stmt); + } + result = sqlite3_close(database); + if (result!=SQLITE_OK) + printf("Unable to close the SQLite Database\n"); + } + + result = sqlite3_close(seenDatabase); + if (result==SQLITE_BUSY) { + sqlite3_stmt *stmt; + while ((stmt = sqlite3_next_stmt(seenDatabase, NULL))!=NULL) { + sqlite3_finalize(stmt); + } + result = sqlite3_close(seenDatabase); + if (result!=SQLITE_OK) + printf("Unable to close the SQLite Seen Database\n"); + } + } + + CString GetUNIXTime() { + struct timeval time; + gettimeofday(&time, NULL); + double microtime = (double)(time.tv_sec + (time.tv_usec/1000000.00)); + char timeStr[25]; + snprintf(timeStr, sizeof(timeStr), "%f", microtime); + return timeStr; + } + + void AddMessage(const CString& target, const CString& nick, const CString& type, const CString& message) { + if (IsIgnored("nick",nick) || (target.Left(1).Equals("#") && IsIgnored("chan",target))) + return; + sqlite3_stmt *result; + int status = sqlite3_prepare(database, "INSERT INTO `messages` (`target`, `nick`, `type`, `message`, `time`) VALUES (?,?,?,?,?)", -1, &result, NULL); + if (status!=SQLITE_OK) + return; + status = sqlite3_bind_text(result, 1, target.c_str(), target.length(), SQLITE_STATIC); + if (status!=SQLITE_OK) { + sqlite3_finalize(result); + return; + } + status = sqlite3_bind_text(result, 2, nick.c_str(), nick.length(), SQLITE_STATIC); + if (status!=SQLITE_OK) { + sqlite3_finalize(result); + return; + } + status = sqlite3_bind_text(result, 3, type.c_str(), type.length(), SQLITE_STATIC); + if (status!=SQLITE_OK) { + sqlite3_finalize(result); + return; + } + status = sqlite3_bind_text(result, 4, message.c_str(), message.length(), SQLITE_STATIC); + if (status!=SQLITE_OK) { + sqlite3_finalize(result); + return; + } + CString time = GetUNIXTime(); + status = sqlite3_bind_text(result, 5, time.c_str(), time.length(), SQLITE_STATIC); + if (status!=SQLITE_OK) { + sqlite3_finalize(result); + return; + } + sqlite3_step(result); + sqlite3_finalize(result); + + if (!nick.Equals("") && target.Left(1).Equals("#")) { + bool exists = false; + CString nickName = nick.substr(0, nick.find('!')); + CString lastAction = "0.0"; + CString action = ""; + CString lastJoin = "0.0"; + CString lastPart = "0.0"; + status = sqlite3_prepare(seenDatabase, "SELECT `lastAction`,`action`,`lastJoin`,`lastPart` FROM `log` WHERE `nick`=? COLLATE NOCASE", -1, &result, NULL); + if (status==SQLITE_OK) { + status = sqlite3_bind_text(result, 1, nickName.c_str(), nickName.length(), SQLITE_STATIC); + if (status==SQLITE_OK) { + status = SQLITE_BUSY; + while (status==SQLITE_BUSY) { + status = sqlite3_step(result); + } + if (status==SQLITE_ROW) { + exists = true; + int dataCount = sqlite3_data_count(result); + if (dataCount==4) { + const char *lastActionC = (const char *)sqlite3_column_text(result, 0); + lastAction = CString(lastActionC); + const char *actionC = (const char *)sqlite3_column_text(result, 1); + action = CString(actionC); + const char *lastJoinC = (const char *)sqlite3_column_text(result, 2); + lastJoin = CString(lastJoinC); + const char *lastPartC = (const char *)sqlite3_column_text(result, 3); + lastPart = CString(lastPartC); + } + } + } + sqlite3_finalize(result); + } + + if (type.Equals("JOIN")) { + lastJoin = time; + } else if (type.Equals("PART") || type.Equals("QUIT")) { + lastPart = time; + } else { + action = target+","+type; + if (type.Equals("NICK")) { + action += ","+message; + } + lastAction = time; + } + + if (exists) { + status = sqlite3_prepare(seenDatabase, "UPDATE `log` SET `lastAction`=?,`action`=?,`lastJoin`=?,`lastPart`=? WHERE `nick`=? COLLATE NOCASE", -1, &result, NULL); + if (status!=SQLITE_OK) { + goto continueLog; + } + status = sqlite3_bind_text(result, 1, lastAction.c_str(), lastAction.length(), SQLITE_STATIC); + if (status!=SQLITE_OK) { + sqlite3_finalize(result); + goto continueLog; + } + status = sqlite3_bind_text(result, 2, action.c_str(), action.length(), SQLITE_STATIC); + if (status!=SQLITE_OK) { + sqlite3_finalize(result); + goto continueLog; + } + status = sqlite3_bind_text(result, 3, lastJoin.c_str(), lastJoin.length(), SQLITE_STATIC); + if (status!=SQLITE_OK) { + sqlite3_finalize(result); + goto continueLog; + } + status = sqlite3_bind_text(result, 4, lastPart.c_str(), lastPart.length(), SQLITE_STATIC); + if (status!=SQLITE_OK) { + sqlite3_finalize(result); + goto continueLog; + } + status = sqlite3_bind_text(result, 5, nickName.c_str(), nickName.length(), SQLITE_STATIC); + if (status!=SQLITE_OK) { + sqlite3_finalize(result); + goto continueLog; + } + sqlite3_step(result); + sqlite3_finalize(result); + } else { + status = sqlite3_prepare(seenDatabase, "INSERT INTO `log` (`lastAction`,`action`,`lastJoin`,`lastPart`,`nick`) VALUES (?,?,?,?,?)", -1, &result, NULL); + if (status!=SQLITE_OK) { + goto continueLog; + } + status = sqlite3_bind_text(result, 1, lastAction.c_str(), lastAction.length(), SQLITE_STATIC); + if (status!=SQLITE_OK) { + sqlite3_finalize(result); + goto continueLog; + } + status = sqlite3_bind_text(result, 2, action.c_str(), action.length(), SQLITE_STATIC); + if (status!=SQLITE_OK) { + sqlite3_finalize(result); + goto continueLog; + } + status = sqlite3_bind_text(result, 3, lastJoin.c_str(), lastJoin.length(), SQLITE_STATIC); + if (status!=SQLITE_OK) { + sqlite3_finalize(result); + goto continueLog; + } + status = sqlite3_bind_text(result, 4, lastPart.c_str(), lastPart.length(), SQLITE_STATIC); + if (status!=SQLITE_OK) { + sqlite3_finalize(result); + goto continueLog; + } + status = sqlite3_bind_text(result, 5, nickName.c_str(), nickName.length(), SQLITE_STATIC); + if (status!=SQLITE_OK) { + sqlite3_finalize(result); + goto continueLog; + } + sqlite3_step(result); + sqlite3_finalize(result); + } + } + + continueLog: + + if (logLimit>=2) { + status = sqlite3_prepare(database, "SELECT COUNT(*) FROM `messages`", -1, &result, NULL); + if (status!=SQLITE_OK) + return; + status = SQLITE_BUSY; + while (status==SQLITE_BUSY) { + status = sqlite3_step(result); + } + if (status!=SQLITE_ROW) + return; + + int dataCount = sqlite3_data_count(result); + if (dataCount!=1) { + sqlite3_finalize(result); + std::cout << "LogSQLite: We are only suppose to receive 1 field. We received the count of " << dataCount << " fields.\n"; + return; + } + unsigned long count = strtoul((const char *)sqlite3_column_text(result, 0), NULL, 10); + if (count<=logLimit) + count = 0; + else + count = count-logLimit; + sqlite3_finalize(result); + + if (count!=0) { + char *queryStr = (char *)malloc(73); + snprintf(queryStr, 73, "SELECT `rowid` FROM `messages` ORDER BY `time` LIMIT %lu", count); + status = sqlite3_prepare(database, queryStr, -1, &result, NULL); + free(queryStr); + if (status!=SQLITE_OK) + return; + + while (true) { + status = SQLITE_BUSY; + while (status==SQLITE_BUSY) { + status = sqlite3_step(result); + } + if (status!=SQLITE_ROW) + break; + + dataCount = sqlite3_data_count(result); + if (dataCount!=1) { + sqlite3_finalize(result); + std::cout << "LogSQLite: We are only suppose to receive 1 field. We received the count of " << dataCount << " fields.\n"; + break; + } + + sqlite3_stmt *result2; + const char *rowid = (const char *)sqlite3_column_text(result, 0); + + status = sqlite3_prepare(database, "DELETE FROM `messages` WHERE `rowid`=?", -1, &result2, NULL); + if (status!=SQLITE_OK) + continue; + status = sqlite3_bind_text(result2, 1, rowid, strlen(rowid), SQLITE_STATIC); + if (status!=SQLITE_OK) { + sqlite3_finalize(result2); + continue; + } + sqlite3_step(result2); + sqlite3_finalize(result2); + } + sqlite3_finalize(result); + } + } + } + + void SetSetting(const CString& name, const CString& value) { + bool exists = false; + sqlite3_stmt *result; + int status = sqlite3_prepare(database, "SELECT `value` FROM `settings` WHERE `name`=?", -1, &result, NULL); + if (status==SQLITE_OK) { + status = sqlite3_bind_text(result, 1, name.c_str(), name.length(), SQLITE_STATIC); + if (status==SQLITE_OK) { + status = SQLITE_BUSY; + while (status==SQLITE_BUSY) { + status = sqlite3_step(result); + } + if (status==SQLITE_ROW) { + exists = true; + } + } + sqlite3_finalize(result); + } + + if (exists) { + status = sqlite3_prepare(database, "UPDATE `settings` SET `value`=? WHERE `name`=?", -1, &result, NULL); + if (status!=SQLITE_OK) { + return; + } + status = sqlite3_bind_text(result, 1, value.c_str(), value.length(), SQLITE_STATIC); + if (status!=SQLITE_OK) { + sqlite3_finalize(result); + return; + } + status = sqlite3_bind_text(result, 2, name.c_str(), name.length(), SQLITE_STATIC); + if (status!=SQLITE_OK) { + sqlite3_finalize(result); + return; + } + sqlite3_step(result); + sqlite3_finalize(result); + } else { + status = sqlite3_prepare(database, "INSERT INTO `settings` (`name`,`value`) VALUES (?,?)", -1, &result, NULL); + if (status!=SQLITE_OK) { + return; + } + status = sqlite3_bind_text(result, 1, name.c_str(), name.length(), SQLITE_STATIC); + if (status!=SQLITE_OK) { + sqlite3_finalize(result); + return; + } + status = sqlite3_bind_text(result, 2, value.c_str(), value.length(), SQLITE_STATIC); + if (status!=SQLITE_OK) { + sqlite3_finalize(result); + return; + } + sqlite3_step(result); + sqlite3_finalize(result); + } + } + CString GetSetting(const CString& name) { + CString stringValue; + sqlite3_stmt *result; + int status = sqlite3_prepare(database, "SELECT `value` FROM `settings` WHERE `name`=?", -1, &result, NULL); + if (status!=SQLITE_OK) + return stringValue; + status = sqlite3_bind_text(result, 1, name.c_str(), name.length(), SQLITE_STATIC); + if (status!=SQLITE_OK) { + sqlite3_finalize(result); + return stringValue; + } + while (true) { + status = SQLITE_BUSY; + while (status==SQLITE_BUSY) { + status = sqlite3_step(result); + } + if (status!=SQLITE_ROW) + break; + + int dataCount = sqlite3_data_count(result); + if (dataCount!=1) { + sqlite3_finalize(result); + std::cout << "LogSQLite: Settings are only suppose to return 1 as the field count. We received the count of " << dataCount << " fields.\n"; + return stringValue; + } + stringValue = CString((const char *)sqlite3_column_text(result, 0)); + break; + } + sqlite3_finalize(result); + return stringValue; + } + + + void UpdateIgnoreLists() { + nickIgnoreList.clear(); + sqlite3_stmt *result; + int status = sqlite3_prepare(database, "SELECT `target` FROM `ignorelist` WHERE `type`='nick'", -1, &result, NULL); + if (status==SQLITE_OK) { + while (true) { + status = SQLITE_BUSY; + while (status==SQLITE_BUSY) { + status = sqlite3_step(result); + } + if (status!=SQLITE_ROW) + break; + + int dataCount = sqlite3_data_count(result); + if (dataCount!=1) + break; + + CString ignore = CString((const char *)sqlite3_column_text(result, 0)); + nickIgnoreList.push_back(ignore); + } + sqlite3_finalize(result); + } + chanIgnoreList.clear(); + status = sqlite3_prepare(database, "SELECT `target` FROM `ignorelist` WHERE `type`='chan'", -1, &result, NULL); + if (status==SQLITE_OK) { + while (true) { + status = SQLITE_BUSY; + while (status==SQLITE_BUSY) { + status = sqlite3_step(result); + } + if (status!=SQLITE_ROW) + break; + + int dataCount = sqlite3_data_count(result); + if (dataCount!=1) + break; + + CString ignore = CString((const char *)sqlite3_column_text(result, 0)); + chanIgnoreList.push_back(ignore); + } + sqlite3_finalize(result); + } + } + bool AddIgnore(const CString& type, const CString& target) { + if (!IgnoreExists(type, target)) { + sqlite3_stmt *result; + int status = sqlite3_prepare(database, "INSERT INTO `ignorelist` (`type`, `target`) VALUES (?,?)", -1, &result, NULL); + if (status!=SQLITE_OK) + return false; + status = sqlite3_bind_text(result, 1, type.c_str(), type.length(), SQLITE_STATIC); + if (status!=SQLITE_OK) { + sqlite3_finalize(result); + return false; + } + status = sqlite3_bind_text(result, 2, target.c_str(), target.length(), SQLITE_STATIC); + if (status!=SQLITE_OK) { + sqlite3_finalize(result); + return false; + } + sqlite3_step(result); + sqlite3_finalize(result); + + if (type.Equals("nick")) + nickIgnoreList.push_back(target); + else if (type.Equals("chan")) + chanIgnoreList.push_back(target); + return true; + } + return false; + } + bool RemoveIgnore(const CString& type, const CString& target) { + if (IgnoreExists(type, target)) { + sqlite3_stmt *result; + int status = sqlite3_prepare(database, "DELETE FROM `ignorelist` WHERE `type`=? AND `target`=?", -1, &result, NULL); + if (status!=SQLITE_OK) + return false; + status = sqlite3_bind_text(result, 1, type.c_str(), type.length(), SQLITE_STATIC); + if (status!=SQLITE_OK) { + sqlite3_finalize(result); + return false; + } + status = sqlite3_bind_text(result, 2, target.c_str(), target.length(), SQLITE_STATIC); + if (status!=SQLITE_OK) { + sqlite3_finalize(result); + return false; + } + sqlite3_step(result); + sqlite3_finalize(result); + + UpdateIgnoreLists(); + return true; + } + return false; + } + bool IgnoreExists(const CString& type, const CString& target) { + if (type.Equals("nick")) { + for (std::vector::iterator it=nickIgnoreList.begin(); it::iterator it=chanIgnoreList.begin(); it::iterator it=nickIgnoreList.begin(); it::iterator it=chanIgnoreList.begin(); it=3) { + AddMessage("","","DISCONNECT",""); + } + } + } + + virtual void OnIRCConnected() { + if (!connected) { + connected = true; + if (logLevel>=3) { + AddMessage("","","CONNECT",""); + } + } + } + + //User stuff + virtual EModRet OnUserAction(CString& sTarget, CString& sMessage) { + if (logLevel>=4) { + AddMessage(sTarget,m_pNetwork->GetIRCNick().GetNickMask(),"ACTION",sMessage); + } + return CONTINUE; + } + + virtual EModRet OnUserMsg(CString& sTarget, CString& sMessage) { + if (logLevel>=4) { + AddMessage(sTarget,m_pNetwork->GetIRCNick().GetNickMask(),"PRIVMSG",sMessage); + } + return CONTINUE; + } + + virtual EModRet OnUserNotice(CString& sTarget, CString& sMessage) { + if (logLevel>=4) { + AddMessage(sTarget,m_pNetwork->GetIRCNick().GetNickMask(),"NOTICE",sMessage); + } + return CONTINUE; + } + + virtual EModRet OnUserJoin(CString& sChannel, CString& sKey) { + if (logLevel>=4) { + AddMessage(sChannel,m_pNetwork->GetIRCNick().GetNickMask(),"JOIN",""); + } + return CONTINUE; + } + + virtual EModRet OnUserPart(CString& sChannel, CString& sMessage) { + if (logLevel>=4) { + AddMessage(sChannel,m_pNetwork->GetIRCNick().GetNickMask(),"PART",sMessage); + } + return CONTINUE; + } + + virtual EModRet OnUserTopic(CString& sChannel, CString& sTopic) { + if (logLevel>=4) { + AddMessage(sChannel,m_pNetwork->GetIRCNick().GetNickMask(),"TOPIC",sTopic); + } + return CONTINUE; + } + + + //Other stuff + virtual void OnRawMode(const CNick& OpNick, CChan& Channel, const CString& sModes, const CString& sArgs) { + if (logLevel>=3) { + AddMessage(Channel.GetName(),OpNick.GetNickMask(),"MODE",sModes+" "+sArgs); + } + } + + virtual void OnKick(const CNick& OpNick, const CString& sKickedNick, CChan& Channel, const CString& sMessage) { + if (logLevel>=2) { + AddMessage(Channel.GetName(),OpNick.GetNickMask(),"KICK",sKickedNick+" "+sMessage); + } + } + + virtual void OnQuit(const CNick& Nick, const CString& sMessage, const std::vector& vChans) { + if (logLevel>=2) { + std::vector::const_iterator it; + for (it=vChans.begin(); it!=vChans.end(); it++) { + CChan& channel = **it; + AddMessage(channel.GetName(),Nick.GetNickMask(),"QUIT",sMessage); + } + } + } + + virtual void OnJoin(const CNick& Nick, CChan& Channel) { + if (logLevel>=2) { + AddMessage(Channel.GetName(),Nick.GetNickMask(),"JOIN",""); + } + } + + virtual void OnPart(const CNick& Nick, CChan& Channel, const CString& sMessage) { + if (logLevel>=2) { + AddMessage(Channel.GetName(),Nick.GetNickMask(),"PART",sMessage); + } + } + + virtual void OnNick(const CNick& OldNick, const CString& sNewNick, const std::vector& vChans) { + if (logLevel>=2) { + std::vector::const_iterator it; + for (it=vChans.begin(); it!=vChans.end(); it++) { + CChan& channel = **it; + AddMessage(channel.GetName(),OldNick.GetNickMask(),"NICK",sNewNick); + } + } + } + + virtual EModRet OnPrivAction(CNick& Nick, CString& sMessage) { + if (logLevel>=0) { + AddMessage(m_pNetwork->GetCurNick(),Nick.GetNickMask(),"ACTION",sMessage); + } + return CONTINUE; + } + + virtual EModRet OnChanAction(CNick& Nick, CChan& Channel, CString& sMessage) { + if (logLevel==0) { + if (strcasestr(sMessage.c_str(),m_pNetwork->GetCurNick().c_str())) + AddMessage(Channel.GetName(),Nick.GetNickMask(),"ACTION",sMessage); + } else if (logLevel>=1) { + AddMessage(Channel.GetName(),Nick.GetNickMask(),"ACTION",sMessage); + } + return CONTINUE; + } + + virtual EModRet OnPrivMsg(CNick& Nick, CString& sMessage) { + if (logLevel>=0) { + AddMessage(m_pNetwork->GetCurNick(),Nick.GetNickMask(),"PRIVMSG",sMessage); + } + return CONTINUE; + } + + virtual EModRet OnChanMsg(CNick& Nick, CChan& Channel, CString& sMessage) { + if (logLevel==0) { + if (strcasestr(sMessage.c_str(),m_pNetwork->GetCurNick().c_str())) + AddMessage(Channel.GetName(),Nick.GetNickMask(),"PRIVMSG",sMessage); + } else if (logLevel>=1) { + AddMessage(Channel.GetName(),Nick.GetNickMask(),"PRIVMSG",sMessage); + } + return CONTINUE; + } + + virtual EModRet OnPrivNotice(CNick& Nick, CString& sMessage) { + if (logLevel>=2) { + AddMessage(m_pNetwork->GetCurNick(),Nick.GetNickMask(),"NOTICE",sMessage); + } + return CONTINUE; + } + + virtual EModRet OnChanNotice(CNick& Nick, CChan& Channel, CString& sMessage) { + if (logLevel>=2) { + AddMessage(Channel.GetName(),Nick.GetNickMask(),"NOTICE",sMessage); + } + return CONTINUE; + } + + virtual EModRet OnTopic(CNick& Nick, CChan& Channel, CString& sTopic) { + if (logLevel>=2) { + AddMessage(Channel.GetName(),Nick.GetNickMask(),"TOPIC",sTopic); + } + return CONTINUE; + } + + virtual EModRet OnRaw(CString& sLine) { + if (logLevel>=3) { + CString sCmd = sLine.Token(1); + if ((sCmd.length() == 3) && (isdigit(sCmd[0])) && (isdigit(sCmd[1])) && (isdigit(sCmd[2]))) { + unsigned int uRaw = sCmd.ToUInt(); + 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) { + AddMessage("",sLine.Token(0),sCmd,sLine.Token(2, true)); + } + } + } + return CONTINUE; + } + + //Client Connection + virtual void OnClientLogin() { + SetSetting("clientConnected",GetUNIXTime()); + if (autoReplay) + Replay(); + } + + virtual void OnClientDisconnect() { + bool track = true; + for (std::vector::iterator it=doNotTrackClient.begin(); itGetIRCNick().GetNickMask()+" :Buffer Playback..."); + + CString lastOnline = GetSetting("clientDisconnected"); + + sqlite3_stmt *result; + int status = SQLITE_OK; + if (replayCount!=0) { + status = sqlite3_prepare(database, "SELECT * FROM (SELECT * FROM `messages` ORDER BY `time` DESC LIMIT ?) ORDER BY `time`", -1, &result, NULL); + if (status!=SQLITE_OK) { + PutUser(":*LogSQLite!LogSQLite@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Playback failed due to sql problem."); + return; + } + status = sqlite3_bind_int(result, 1, replayCount); + if (status!=SQLITE_OK) { + PutUser(":*LogSQLite!LogSQLite@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Playback failed due to sql problem."); + sqlite3_finalize(result); + return; + } + } else if (!replayAll && !lastOnline.empty()) { + status = sqlite3_prepare(database, "SELECT * FROM `messages` WHERE `time`>? ORDER BY `time`", -1, &result, NULL); + if (status!=SQLITE_OK) { + PutUser(":*LogSQLite!LogSQLite@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Playback failed due to sql problem."); + return; + } + status = sqlite3_bind_text(result, 1, lastOnline.c_str(), lastOnline.length(), SQLITE_STATIC); + if (status!=SQLITE_OK) { + PutUser(":*LogSQLite!LogSQLite@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Playback failed due to sql problem."); + sqlite3_finalize(result); + return; + } + } else { + status = sqlite3_prepare(database, "SELECT * FROM `messages` ORDER BY `time`", -1, &result, NULL); + if (status!=SQLITE_OK) { + PutUser(":*LogSQLite!LogSQLite@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Playback failed due to sql problem."); + return; + } + } + + int columnCount = sqlite3_column_count(result); + std::map columns; + for (int i=0; i data; + int dataCount = sqlite3_data_count(result); + for (int i=0; iHasServerTime()) { + char timeStr[20]; + snprintf(timeStr, sizeof(timeStr), "%lu", unixTime); + prefixString = "@t="; + prefixString += timeStr; + prefixString += " "; + } else { + struct tm *timeinfo = localtime(&unixTime); + char timeStr[20]; + if (((long)now-86000)<(long)time) + strftime(timeStr, sizeof(timeStr), "%I:%M:%S %p", timeinfo); + else + strftime(timeStr, sizeof(timeStr), "%m/%d/%y %I:%M:%S %p", timeinfo); + timeString = "["; + timeString += timeStr; + timeString += "] "; + } + + if (data["type"].Equals("DISCONNECT")) { + PutUser(prefixString+":*LogSQLite!LogSQLite@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :"+timeString+"Server Disconnected"); + } else if (data["type"].Equals("CONNECT")) { + PutUser(prefixString+":*LogSQLite!LogSQLite@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :"+timeString+"Server Connected"); + } else if (data["type"].Equals("JOIN")) { + PutUser(prefixString+":"+data["nick"]+" NOTICE "+data["target"]+" :"+timeString+"joined"); + } else if (data["type"].Equals("PART")) { + if (data["message"].Equals("")) + PutUser(prefixString+":"+data["nick"]+" NOTICE "+data["target"]+" :"+timeString+"parted"); + else + PutUser(prefixString+":"+data["nick"]+" NOTICE "+data["target"]+" :"+timeString+"parted: "+data["message"]); + } else if (data["type"].Equals("TOPIC")) { + PutUser(prefixString+":"+data["nick"]+" NOTICE "+data["target"]+" :"+timeString+"changed topic: "+data["message"]); + } else if (data["type"].Equals("QUIT")) { + if (data["message"].Equals("")) + PutUser(prefixString+":"+data["nick"]+" NOTICE "+data["target"]+" :"+timeString+"quit"); + else + PutUser(prefixString+":"+data["nick"]+" NOTICE "+data["target"]+" :"+timeString+"quit: "+data["message"]); + } else if (data["type"].Equals("MODE")) { + PutUser(prefixString+":"+data["nick"]+" NOTICE "+data["target"]+" :"+timeString+"changed mode: "+data["message"]); + } else if (data["type"].Equals("NICK")) { + PutUser(prefixString+":"+data["nick"]+" NOTICE "+data["target"]+" :"+timeString+"changed nick: "+data["message"]); + } else if (data["type"].Equals("ACTION")) { + PutUser(prefixString+":"+data["nick"]+" PRIVMSG "+data["target"]+" :\001ACTION "+timeString+data["message"]+"\001"); + } else { + PutUser(prefixString+":"+data["nick"]+" "+data["type"]+" "+data["target"]+" :"+timeString+data["message"]); + } + } + + sqlite3_finalize(result); + + if (logLimit==1) { + status = sqlite3_prepare(database, "DELETE FROM `messages`", -1, &result, NULL); + if (status==SQLITE_OK) { + sqlite3_step(result); + sqlite3_finalize(result); + } + } + + PutUser(":*LogSQLite!LogSQLite@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Playback Complete."); + } + +private: + sqlite3 *database; + sqlite3 *seenDatabase; + bool connected; + bool replayAll; + bool autoReplay; + unsigned long logLimit; + int logLevel; + + std::vector nickIgnoreList; + std::vector chanIgnoreList; + + std::vector doNotTrackClient; +}; + +template<> void TModInfo(CModInfo& Info) { + Info.SetWikiPage("logsqlite"); +} + +NETWORKMODULEDEFS(CLogSQLite, "Add logging to SQLite") \ No newline at end of file diff --git a/logsqlite.cpp b/logsqlite.cpp index dfae1c1..f2afe14 100755 --- a/logsqlite.cpp +++ b/logsqlite.cpp @@ -30,8 +30,9 @@ class CLogSQLite : public CModule { public: MODCONSTRUCTOR(CLogSQLite) { AddHelpCommand(); - AddCommand("Replay", static_cast(&CLogSQLite::ReplayCommand), "", "Play back the messages received."); - AddCommand("ReplayAll", static_cast(&CLogSQLite::ReplayAllCommand), "[1|0]", "Replay all messages stored."); + AddCommand("Replay", static_cast(&CLogSQLite::ReplayCommand), "[0-9]*", "Play back messages received since last recorded disconnect or to count."); + AddCommand("ReplayAll", static_cast(&CLogSQLite::ReplayAllCommand), "[1|0]", "Replay all messages stored."); + AddCommand("AutoReplay", static_cast(&CLogSQLite::AutoReplayCommand), "[1|0]", "Replay on connect."); AddCommand("LogLimit", static_cast(&CLogSQLite::LogLimitCommand), "[0-9]+", "Limit the amount of items to store into the log."); AddCommand("LogLevel", static_cast(&CLogSQLite::LogLevelCommand), "[0-4]", "Log level."); @@ -43,7 +44,12 @@ public: } void ReplayCommand(const CString &sLine) { - Replay(); + CString sArgs = sLine.Token(1, true); + if (!sArgs.empty()) { + Replay(sArgs.ToInt()); + } else { + Replay(); + } PutModule("Replayed"); } @@ -61,9 +67,26 @@ public: CString status = (replayAll ? "On" : "Off"); CString now = (sArgs.empty() || help ? "" : "now "); - PutModule("ReplayAll is "+now+"set to: "+=status); + PutModule("ReplayAll is "+now+"set to: "+status); } - + + void AutoReplayCommand(const CString &sLine) { + CString sArgs = sLine.Token(1, true); + bool help = sArgs.Equals("HELP"); + + if (help) { + PutModule("On: Replay on connect."); + PutModule("Off: Require replay command to replay."); + } else if (!sArgs.empty()) { + autoReplay = sArgs.ToBool(); + SetSetting("autoReplay", (autoReplay ? "1" : "0")); + } + + CString status = (autoReplay ? "On" : "Off"); + CString now = (sArgs.empty() || help ? "" : "now "); + PutModule("AutoReplay is "+now+"set to: "+status); + } + void LogLimitCommand(const CString &sLine) { CString sArgs = sLine.Token(1, true); bool help = sArgs.Equals("HELP"); @@ -182,7 +205,7 @@ public: } else { PutModule("Nick ignore list contains:"); - for (vector::iterator it=nickIgnoreList.begin(); it::iterator it=nickIgnoreList.begin(); it::iterator it=chanIgnoreList.begin(); it::iterator it=chanIgnoreList.begin(); it::iterator it=doNotTrackClient.begin(); it::iterator it=doNotTrackClient.begin(); it::iterator it=nickIgnoreList.begin(); it::iterator it=nickIgnoreList.begin(); it::iterator it=chanIgnoreList.begin(); it::iterator it=chanIgnoreList.begin(); it::iterator it=nickIgnoreList.begin(); it::iterator it=nickIgnoreList.begin(); it::iterator it=chanIgnoreList.begin(); it::iterator it=chanIgnoreList.begin(); it& vChans) { + virtual void OnQuit(const CNick& Nick, const CString& sMessage, const std::vector& vChans) { if (logLevel>=2) { - vector::const_iterator it; + std::vector::const_iterator it; for (it=vChans.begin(); it!=vChans.end(); it++) { CChan& channel = **it; AddMessage(channel.GetName(),Nick.GetNickMask(),"QUIT",sMessage); @@ -761,13 +791,13 @@ public: virtual void OnPart(const CNick& Nick, CChan& Channel, const CString& sMessage) { if (logLevel>=2) { - AddMessage(Channel.GetName(),Nick.GetNickMask(),"JOIN",sMessage); + AddMessage(Channel.GetName(),Nick.GetNickMask(),"PART",sMessage); } } - virtual void OnNick(const CNick& OldNick, const CString& sNewNick, const vector& vChans) { + virtual void OnNick(const CNick& OldNick, const CString& sNewNick, const std::vector& vChans) { if (logLevel>=2) { - vector::const_iterator it; + std::vector::const_iterator it; for (it=vChans.begin(); it!=vChans.end(); it++) { CChan& channel = **it; AddMessage(channel.GetName(),OldNick.GetNickMask(),"NICK",sNewNick); @@ -846,12 +876,13 @@ public: //Client Connection virtual void OnClientLogin() { SetSetting("clientConnected",GetUNIXTime()); - Replay(); + if (autoReplay) + Replay(); } virtual void OnClientDisconnect() { bool track = true; - for (vector::iterator it=doNotTrackClient.begin(); it::iterator it=doNotTrackClient.begin(); itGetIRCNick().GetNickMask()+" :Buffer Playback..."); CString lastOnline = GetSetting("clientDisconnected"); sqlite3_stmt *result; int status = SQLITE_OK; - if (!replayAll && !lastOnline.empty()) { + if (replayCount!=0) { + status = sqlite3_prepare(database, "SELECT * FROM (SELECT * FROM `messages` ORDER BY `time` DESC LIMIT ?) ORDER BY `time`", -1, &result, NULL); + if (status!=SQLITE_OK) { + PutUser(":*LogSQLite!LogSQLite@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Playback failed due to sql problem."); + return; + } + status = sqlite3_bind_int(result, 1, replayCount); + if (status!=SQLITE_OK) { + PutUser(":*LogSQLite!LogSQLite@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Playback failed due to sql problem."); + sqlite3_finalize(result); + return; + } + } else if (!replayAll && !lastOnline.empty()) { status = sqlite3_prepare(database, "SELECT * FROM `messages` WHERE `time`>? ORDER BY `time`", -1, &result, NULL); if (status!=SQLITE_OK) { PutUser(":*LogSQLite!LogSQLite@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Playback failed due to sql problem."); @@ -890,7 +937,7 @@ public: } int columnCount = sqlite3_column_count(result); - map columns; + std::map columns; for (int i=0; i data; + std::map data; int dataCount = sqlite3_data_count(result); for (int i=0; i nickIgnoreList; - vector chanIgnoreList; + std::vector nickIgnoreList; + std::vector chanIgnoreList; - vector doNotTrackClient; + std::vector doNotTrackClient; }; template<> void TModInfo(CModInfo& Info) {