LogSQL Start

This commit is contained in:
GRMrGecko 2012-02-11 13:35:43 -06:00
commit 902834079b
6 changed files with 1640 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.so

7
License.txt Normal file
View File

@ -0,0 +1,7 @@
ISC License (ISCL)
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.

26
buildLogMySQL Executable file
View File

@ -0,0 +1,26 @@
#!/bin/bash
ZNCBUILD=`which znc-buildmod`
if [ "$ZNCBUILD" = "" ]; then
echo "Where is znc-buildmod:"
read ZNCBUILD
if [ "$ZNCBUILD" = "" ]; then
exit
fi
fi
echo "Where is MySQL path [/usr/local/mysql]:"
read MYSQL
if [ "$MYSQL" = "" ]; then
MYSQL="/usr/local/mysql"
fi
echo "Where is ZNC configuration path [$HOME/.znc]:"
read ZNCCONFIG
if [ "$ZNCCONFIG" = "" ]; then
ZNCCONFIG="$HOME/.znc"
fi
LDFLAGS="-L $MYSQL/lib -lmysqlclient -I $MYSQL/include" $ZNCBUILD logmysql.cpp
install_name_tool -change libmysqlclient.18.dylib "$MYSQL/libmysqlclient.dylib" logmysql.so
cp logmysql.so "$ZNCCONFIG/modules/logmysql.so"

25
buildLogSQLite Executable file
View File

@ -0,0 +1,25 @@
#!/bin/bash
ZNCBUILD=`which znc-buildmod`
if [ "$ZNCBUILD" = "" ]; then
echo "Where is znc-buildmod:"
read ZNCBUILD
if [ "$ZNCBUILD" = "" ]; then
exit
fi
fi
echo "Where is SQLite path [/usr]:"
read SQLITE
if [ "$SQLITE" = "" ]; then
SQLITE="/usr"
fi
echo "Where is ZNC configuration path [$HOME/.znc]:"
read ZNCCONFIG
if [ "$ZNCCONFIG" = "" ]; then
ZNCCONFIG="$HOME/.znc"
fi
LDFLAGS="-L $SQLITE/lib -lsqlite3 -I $SQLITE/include" $ZNCBUILD logsqlite.cpp
cp logsqlite.so "$ZNCCONFIG/modules/logsqlite.so"

937
logmysql.cpp Executable file
View File

@ -0,0 +1,937 @@
//
// logmysql.cpp
// LogSQL
//
// Created by Mr. Gecko on 2/10/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 <znc/Chan.h>
#include <znc/User.h>
#include <znc/IRCNetwork.h>
#include <znc/Modules.h>
#include <time.h>
#import <mysql.h>
class CLogMySQL : public CModule {
public:
MODCONSTRUCTOR(CLogMySQL) {}
virtual bool OnLoad(const CString& sArgs, CString& sMessage) {
connected = true;
databaseConnected = false;
MySQLConnect();
return true;
}
virtual ~CLogMySQL() {
if (databaseConnected) {
databaseConnected = false;
mysql_close(database);
database = NULL;
}
}
void MySQLConnect() {
CString host = GetNV("host");
CString port = GetNV("port");
if (port.empty()) {
port = "3306";
SetNV("port",port);
}
CString username = GetNV("username");
CString password = GetNV("password");
CString databaseName = GetNV("databaseName");
if (!host.empty() && !username.empty() && !databaseName.empty()) {
if (databaseConnected) {
databaseConnected = false;
mysql_close(database);
database = NULL;
}
database = mysql_init(NULL);
if (database!=NULL) {
void *theRet = mysql_real_connect(database, host.c_str(), username.c_str(), password.c_str(), databaseName.c_str(), (unsigned int)strtoul(port.c_str(), NULL, 10), MYSQL_UNIX_ADDR, CLIENT_COMPRESS);
databaseConnected = (theRet==database);
if (databaseConnected) {
cout << "LogMySQL: Database connected.\n";
MYSQL_RES *settings = mysql_list_tables(database, "settings");
MYSQL_RES *messages = mysql_list_tables(database, "messages");
if (mysql_num_rows(settings)==0 || mysql_num_rows(messages)==0) {
cout << "Creating tables\n";
MYSQL_STMT *statement = mysql_stmt_init(database);
int status = mysql_stmt_prepare(statement, "DROP TABLE IF EXISTS `settings`", 31);
if (status==0) {
mysql_stmt_execute(statement);
mysql_stmt_close(statement);
}
statement = mysql_stmt_init(database);
status = mysql_stmt_prepare(statement, "CREATE TABLE `settings` (`name` text,`value` text)", 50);
if (status==0) {
mysql_stmt_execute(statement);
mysql_stmt_close(statement);
}
statement = mysql_stmt_init(database);
status = mysql_stmt_prepare(statement, "DROP TABLE IF EXISTS `messages`", 31);
if (status==0) {
mysql_stmt_execute(statement);
mysql_stmt_close(statement);
}
statement = mysql_stmt_init(database);
status = mysql_stmt_prepare(statement, "CREATE TABLE `messages` (`rowid` int UNSIGNED AUTO_INCREMENT,`target` text,`nick` text,`type` text,`message` longblob,`time` decimal(20,5) UNSIGNED,PRIMARY KEY (`rowid`))", 170);
if (status==0) {
mysql_stmt_execute(statement);
mysql_stmt_close(statement);
}
SetSetting("replayAll","0");
SetSetting("logLimit","1");
SetSetting("logLevel","1");
}
if (settings!=NULL)
mysql_free_result(settings);
if (messages!=NULL)
mysql_free_result(messages);
replayAll = atoi(GetSetting("replayAll").c_str());
logLimit = strtoul(GetSetting("logLimit").c_str(), NULL, 10);
logLevel = atoi(GetSetting("logLevel").c_str());
} else {
cout << "LogMySQL: Database unable to connect.\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 (!databaseConnected)
return;
MYSQL_STMT *statement = mysql_stmt_init(database);
int status = mysql_stmt_prepare(statement, "INSERT INTO `messages` (`target`, `nick`, `type`, `message`, `time`) VALUES (?,?,?,?,?)", 87);
if (status!=0)
return;
MYSQL_BIND bind[5];
memset(bind, 0, sizeof(bind));
bind[0].buffer_type = MYSQL_TYPE_STRING;
bind[0].buffer = (void*)target.c_str();
bind[0].buffer_length = target.length();
bind[0].is_null = false;
bind[1].buffer_type = MYSQL_TYPE_STRING;
bind[1].buffer = (void*)nick.c_str();
bind[1].buffer_length = nick.length();
bind[1].is_null = false;
bind[2].buffer_type = MYSQL_TYPE_STRING;
bind[2].buffer = (void*)type.c_str();
bind[2].buffer_length = type.length();
bind[2].is_null = false;
bind[3].buffer_type = MYSQL_TYPE_STRING;
bind[3].buffer = (void*)message.c_str();
bind[3].buffer_length = message.length();
bind[3].is_null = false;
CString time = GetUNIXTime();
bind[4].buffer_type = MYSQL_TYPE_STRING;
bind[4].buffer = (void*)time.c_str();
bind[4].buffer_length = time.length();
bind[4].is_null = false;
status = mysql_stmt_bind_param(statement, bind);
if (status!=0) {
mysql_stmt_close(statement);
return;
}
mysql_stmt_execute(statement);
mysql_stmt_close(statement);
if (logLimit>=2) {
statement = mysql_stmt_init(database);
status = mysql_stmt_prepare(statement, "SELECT COUNT(*) FROM `messages`", 31);
if (status!=0)
return;
status = mysql_stmt_execute(statement);
if (status!=0) {
mysql_stmt_close(statement);
return;
}
MYSQL_RES *result = mysql_stmt_result_metadata(statement);
unsigned int dataCount = mysql_num_fields(result);
if (dataCount!=1) {
mysql_free_result(result);
mysql_stmt_close(statement);
cout << "LogMySQL: We are only suppose to receive 1 field. We received the count of " << dataCount << " fields.\n";
return;
}
MYSQL_FIELD *fields = mysql_fetch_fields(result);
unsigned long length;
char countData[fields[0].length];
MYSQL_BIND results[1];
memset(results, 0, sizeof(results));
results[0].buffer_type = MYSQL_TYPE_STRING;
results[0].buffer = (void *)countData;
results[0].buffer_length = fields[0].length;
results[0].length = &length;
status = mysql_stmt_bind_result(statement, results);
if (status!=0) {
mysql_free_result(result);
mysql_stmt_close(statement);
return;
}
status = mysql_stmt_fetch(statement);
if (status!=0) {
mysql_free_result(result);
mysql_stmt_close(statement);
return;
}
CString countString = CString(countData, length);
unsigned long count = strtoul(countString.c_str(), NULL, 10);
if (count<=logLimit)
count = 0;
else
count = count-logLimit;
mysql_free_result(result);
mysql_stmt_close(statement);
if (count!=0) {
statement = mysql_stmt_init(database);
char *queryStr = (char *)malloc(73);
snprintf(queryStr, 73, "SELECT `rowid` FROM `messages` ORDER BY `time` LIMIT %lu", count);
status = mysql_stmt_prepare(statement, queryStr, strlen(queryStr));
free(queryStr);
if (status!=0)
return;
status = mysql_stmt_execute(statement);
if (status!=0) {
mysql_stmt_close(statement);
return;
}
result = mysql_stmt_result_metadata(statement);
dataCount = mysql_num_fields(result);
if (dataCount!=1) {
mysql_free_result(result);
mysql_stmt_close(statement);
cout << "LogMySQL: We are only suppose to receive 1 field. We received the count of " << dataCount << " fields.\n";
return;
}
fields = mysql_fetch_fields(result);
char rowidData[fields[0].length];
memset(results, 0, sizeof(results));
results[0].buffer_type = MYSQL_TYPE_STRING;
results[0].buffer = (void *)rowidData;
results[0].buffer_length = fields[0].length;
results[0].length = &length;
status = mysql_stmt_bind_result(statement, results);
if (status!=0) {
mysql_free_result(result);
mysql_stmt_close(statement);
return;
}
while (true) {
status = mysql_stmt_fetch(statement);
if (status!=0)
break;
CString rowid = CString(rowidData, length);
MYSQL_STMT *statement2 = mysql_stmt_init(database);
status = mysql_stmt_prepare(statement2, "DELETE FROM `messages` WHERE `rowid`=?", 38);
if (status!=0)
continue;
MYSQL_BIND bind2[1];
memset(bind, 0, sizeof(bind));
bind2[0].buffer_type = MYSQL_TYPE_STRING;
bind2[0].buffer = (void*)rowid.c_str();
bind2[0].buffer_length = rowid.length();
bind2[0].is_null = false;
status = mysql_stmt_bind_param(statement2, bind2);
if (status!=0) {
mysql_stmt_close(statement2);
continue;
}
mysql_stmt_execute(statement2);
mysql_stmt_close(statement2);
}
mysql_free_result(result);
mysql_stmt_close(statement);
}
}
}
void SetSetting(const CString& name, const CString& value) {
if (!databaseConnected)
return;
bool exists = false;
MYSQL_STMT *statement = mysql_stmt_init(database);
int status = mysql_stmt_prepare(statement, "SELECT `value` FROM `settings` WHERE `name`=?", 45);
if (status==0) {
MYSQL_BIND bind[1];
memset(bind, 0, sizeof(bind));
bind[0].buffer_type = MYSQL_TYPE_STRING;
bind[0].buffer = (void*)name.c_str();
bind[0].buffer_length = name.length();
bind[0].is_null = false;
status = mysql_stmt_bind_param(statement, bind);
if (status==0) {
mysql_stmt_execute(statement);
status = mysql_stmt_fetch(statement);
if (status==0) {
exists = true;
}
}
mysql_stmt_close(statement);
}
if (exists) {
statement = mysql_stmt_init(database);
status = mysql_stmt_prepare(statement, "UPDATE `settings` SET `value`=? WHERE `name`=?", 46);
if (status!=0)
return;
MYSQL_BIND bind[2];
memset(bind, 0, sizeof(bind));
bind[0].buffer_type = MYSQL_TYPE_STRING;
bind[0].buffer = (void*)value.c_str();
bind[0].buffer_length = value.length();
bind[0].is_null = false;
bind[1].buffer_type = MYSQL_TYPE_STRING;
bind[1].buffer = (void*)name.c_str();
bind[1].buffer_length = name.length();
bind[1].is_null = false;
status = mysql_stmt_bind_param(statement, bind);
if (status!=0) {
mysql_stmt_close(statement);
return;
}
mysql_stmt_execute(statement);
mysql_stmt_close(statement);
} else {
statement = mysql_stmt_init(database);
status = mysql_stmt_prepare(statement, "INSERT INTO `settings` (`name`,`value`) VALUES (?,?)", 52);
if (status!=0)
return;
MYSQL_BIND bind[2];
memset(bind, 0, sizeof(bind));
bind[0].buffer_type = MYSQL_TYPE_STRING;
bind[0].buffer = (void*)name.c_str();
bind[0].buffer_length = name.length();
bind[0].is_null = false;
bind[1].buffer_type = MYSQL_TYPE_STRING;
bind[1].buffer = (void*)value.c_str();
bind[1].buffer_length = value.length();
bind[1].is_null = false;
status = mysql_stmt_bind_param(statement, bind);
if (status!=0) {
mysql_stmt_close(statement);
return;
}
mysql_stmt_execute(statement);
mysql_stmt_close(statement);
}
}
CString GetSetting(const CString& name) {
CString stringValue;
if (!databaseConnected)
return stringValue;
MYSQL_STMT *statement = mysql_stmt_init(database);
int status = mysql_stmt_prepare(statement, "SELECT `value` FROM `settings` WHERE `name`=?", 45);
if (status!=0)
return stringValue;
MYSQL_BIND bind[1];
memset(bind, 0, sizeof(bind));
bind[0].buffer_type = MYSQL_TYPE_STRING;
bind[0].buffer = (void*)name.c_str();
bind[0].buffer_length = name.length();
bind[0].is_null = false;
status = mysql_stmt_bind_param(statement, bind);
if (status!=0) {
mysql_stmt_close(statement);
return stringValue;
}
status = mysql_stmt_execute(statement);
if (status!=0) {
mysql_stmt_close(statement);
return stringValue;
}
MYSQL_RES *result = mysql_stmt_result_metadata(statement);
unsigned int dataCount = mysql_num_fields(result);
if (dataCount!=1) {
mysql_free_result(result);
mysql_stmt_close(statement);
cout << "LogMySQL: Settings are only suppose to return 1 as the field count. We received the count of " << dataCount << " fields.\n";
return stringValue;
}
MYSQL_FIELD *fields = mysql_fetch_fields(result);
unsigned long length;
char valueData[fields[0].length];
MYSQL_BIND results[1];
memset(results, 0, sizeof(results));
results[0].buffer_type = MYSQL_TYPE_STRING;
results[0].buffer = (void *)valueData;
results[0].buffer_length = fields[0].length;
results[0].length = &length;
status = mysql_stmt_bind_result(statement, results);
if (status!=0) {
mysql_free_result(result);
mysql_stmt_close(statement);
return stringValue;
}
while (true) {
status = mysql_stmt_fetch(statement);
if (status!=0)
break;
stringValue = CString(valueData, length);
break;
}
mysql_free_result(result);
mysql_stmt_close(statement);
return stringValue;
}
//Server stuff
virtual void OnIRCDisconnected() {
if (connected) {
connected = false;
if (logLevel>=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 vector<CChan*>& vChans) {
if (logLevel>=2) {
vector<CChan*>::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(),"JOIN",sMessage);
}
}
virtual void OnNick(const CNick& OldNick, const CString& sNewNick, const vector<CChan*>& vChans) {
if (logLevel>=2) {
vector<CChan*>::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());
Replay();
}
virtual void OnClientDisconnect() {
if (!m_pNetwork->IsUserAttached()) {
SetSetting("clientDisconnected",GetUNIXTime());
}
}
virtual void OnModCommand(const CString& sCmdLine) {
CString sCommand = sCmdLine.Token(0);
CString sArgs = sCmdLine.Token(1, true);
if (sCommand.Equals("HELP")) {
CString host = GetNV("host");
CString port = GetNV("port");
if (port.empty()) {
port = "3306";
SetNV("port",port);
}
CString username = GetNV("username");
CString password = GetNV("password");
CString databaseName = GetNV("databaseName");
PutModule("host - MySQL host.");
PutModule("port - MySQL port (defualt is 3306).");
PutModule("username - MySQL username.");
PutModule("password - MySQL password.");
PutModule("databaseName - MySQL database name.");
PutModule("-----");
PutModule("connect - Reconnect to the MySQL database.");
PutModule("-----");
PutModule("replay - Play back the messages received.");
PutModule("replayAll - Set LogMySQL to replay all messages stored (default is off).");
PutModule("logLimit - Set LogMySQL to limit the amount of items to store into the log (0 means to keep everything, 1 means clear after replay, anything else would limit to the count, default is 1).");
PutModule("logLevel - Set LogMySQL log level (0 messages mentioning you and/or to you only. 1 include all messages sent in an channel. 2 include all actions, join/part, and notices sent in channel and to you. 3 include all server wide messages. 4 include messages sent by you. Default is 1).");
PutModule("-----");
PutModule("Layout is as below.");
PutModule("Command [arguments]");
PutModule("-----");
PutModule("If you do not include an argument on items that supports arguments, LogMySQL will return the current setting for the item.");
} else if (sCommand.Equals("HOST")) {
if (sArgs.empty()) {
CString host = GetNV("host");
PutModule("Host is set to: "+host);
} else {
SetNV("host",sArgs);
PutModule("Host is now set to: "+sArgs);
}
} else if (sCommand.Equals("PORT")) {
if (sArgs.empty()) {
CString port = GetNV("port");
PutModule("Port is set to: "+port);
} else {
CString result;
unsigned int port = strtoul(sArgs.c_str(), NULL, 10);
char portStr[20];
snprintf(portStr, sizeof(portStr), "%u", port);
result = portStr;
SetNV("port",result);
PutModule("Port is now set to: "+result);
}
} else if (sCommand.Equals("USERNAME")) {
if (sArgs.empty()) {
CString username = GetNV("username");
PutModule("Username is set to: "+username);
} else {
SetNV("username",sArgs);
PutModule("Username is now set to: "+sArgs);
}
} else if (sCommand.Equals("PASSWORD")) {
if (sArgs.empty()) {
CString password = GetNV("password");
if (password.empty())
PutModule("Password is blank.");
else
PutModule("Password is set.");
} else {
SetNV("password",sArgs);
PutModule("Password is now set.");
}
} else if (sCommand.Equals("DATABASENAME")) {
if (sArgs.empty()) {
CString databaseName = GetNV("databaseName");
PutModule("Database Name is set to: "+databaseName);
} else {
SetNV("databaseName",sArgs);
PutModule("Database Name is now set to: "+sArgs);
}
} else if (sCommand.Equals("CONNECT")) {
MySQLConnect();
if (databaseConnected)
PutModule("Database is now connected");
else
PutModule("Unable to connect to database. Check configuration.");
} else if (sCommand.Equals("REPLAY")) {
Replay();
PutModule("Replayed");
} else if (sCommand.Equals("REPLAYALL")) {
if (sArgs.empty()) {
CString status = (replayAll ? "On" : "Off");
PutModule("ReplayAll is set to: "+status);
} else {
if (sArgs.Equals("ON") || sArgs.Equals("1") || sArgs.Equals("true")) {
replayAll = true;
SetSetting("replayAll", "1");
} else {
replayAll = false;
SetSetting("replayAll", "0");
}
CString status = (replayAll ? "On" : "Off");
PutModule("ReplayAll is now set to: "+status);
}
} else if (sCommand.Equals("LOGLIMIT")) {
if (sArgs.empty()) {
CString result;
char limitStr[20];
snprintf(limitStr, sizeof(limitStr), "%lu", logLimit);
result = limitStr;
PutModule("LogLimit is set to: "+result);
} else {
logLimit = strtoul(sArgs.c_str(), NULL, 10);
CString result;
char limitStr[20];
snprintf(limitStr, sizeof(limitStr), "%lu", logLimit);
result = limitStr;
SetSetting("logLimit", result);
PutModule("LogLimit is now set to: "+result);
}
} else if (sCommand.Equals("LOGLEVEL")) {
if (sArgs.empty()) {
CString result;
char levelStr[20];
snprintf(levelStr, sizeof(levelStr), "%d", logLevel);
result = levelStr;
PutModule("LogLevel is set to: "+result);
} else {
logLevel = atoi(sArgs.c_str());
CString result;
char levelStr[20];
snprintf(levelStr, sizeof(levelStr), "%d", logLevel);
result = levelStr;
SetSetting("logLevel", result);
PutModule("LogLevel is now set to: "+result);
}
} else
PutModule("Unknown command ["+sCommand+"] for help, type help.");
}
void Replay() {
if (!databaseConnected)
return;
PutUser(":*LogMySQL!LogMySQL@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Buffer Playback...");
CString lastOnline = GetSetting("clientDisconnected");
MYSQL_STMT *statement = mysql_stmt_init(database);
int status = 0;
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.");
return;
}
MYSQL_BIND bind[1];
memset(bind, 0, sizeof(bind));
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;
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 {
status = mysql_stmt_prepare(statement, "SELECT * FROM `messages` ORDER BY `time`", 40);
if (status!=0) {
PutUser(":*LogMySQL!LogMySQL@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Playback failed due to sql problem.");
return;
}
}
status = mysql_stmt_execute(statement);
if (status!=0) {
PutUser(":*LogMySQL!LogMySQL@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Playback failed due to sql problem.");
mysql_stmt_close(statement);
return;
}
MYSQL_RES *result = mysql_stmt_result_metadata(statement);
unsigned int columnCount = mysql_num_fields(result);
MYSQL_FIELD *fields = mysql_fetch_fields(result);
MYSQL_BIND results[columnCount];
memset(results, 0, sizeof(results));
map<unsigned int,char *> columnData;
map<unsigned int,unsigned long> columnLength;
map<unsigned int,bool> columnNull;
map<unsigned int,CString> columns;
for (unsigned int i=0; i<columnCount; i++) {
columns[i] = CString(fields[i].name);
if (columns[i].empty()) {
char count[20];
snprintf(count, sizeof(count), "%u", i);
columns[i] = count;
}
columnData[i] = (char *)malloc(fields[i].length);
results[i].buffer_type = MYSQL_TYPE_STRING;
results[i].buffer = (void *)columnData[i];
results[i].buffer_length = fields[i].length;
results[i].length = &columnLength[i];
results[i].is_null = (my_bool *)&columnNull[i];
}
status = mysql_stmt_bind_result(statement, results);
if (status!=0) {
PutUser(":*LogMySQL!LogMySQL@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Playback failed due to sql problem.");
mysql_free_result(result);
mysql_stmt_close(statement);
return;
}
while (true) {
status = mysql_stmt_fetch(statement);
if (status!=0)
break;
map<CString,CString> data;
for (unsigned int i=0; i<columnCount; i++) {
if (columnNull[i]) {
data[columns[i]] = "";
} else {
data[columns[i]] = CString(columnData[i], columnLength[i]);
}
}
time_t now = time(NULL);
time_t unixTime = (time_t)strtol(data["time"].c_str(),NULL,10);
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);
if (data["type"].Equals("DISCONNECT")) {
PutUser(":*LogMySQL!LogMySQL@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :["+timeStr+"] Server Disconnected");
} else if (data["type"].Equals("CONNECT")) {
PutUser(":*LogMySQL!LogMySQL@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :["+timeStr+"] Server Connected");
} else if (data["type"].Equals("JOIN")) {
PutUser(":"+data["nick"]+" NOTICE "+data["target"]+" :["+timeStr+"] joined");
} else if (data["type"].Equals("PART")) {
if (data["message"].Equals(""))
PutUser(":"+data["nick"]+" NOTICE "+data["target"]+" :["+timeStr+"] parted");
else
PutUser(":"+data["nick"]+" NOTICE "+data["target"]+" :["+timeStr+"] parted: "+data["message"]);
} else if (data["type"].Equals("TOPIC")) {
PutUser(":"+data["nick"]+" NOTICE "+data["target"]+" :["+timeStr+"] changed topic: "+data["message"]);
} else if (data["type"].Equals("QUIT")) {
if (data["message"].Equals(""))
PutUser(":"+data["nick"]+" NOTICE "+data["target"]+" :["+timeStr+"] quit");
else
PutUser(":"+data["nick"]+" NOTICE "+data["target"]+" :["+timeStr+"] quit: "+data["message"]);
} else if (data["type"].Equals("MODE")) {
PutUser(":"+data["nick"]+" NOTICE "+data["target"]+" :["+timeStr+"] changed mode: "+data["message"]);
} else if (data["type"].Equals("ACTION")) {
PutUser(":"+data["nick"]+" PRIVMSG "+data["target"]+" :\001ACTION ["+timeStr+"] "+data["message"]+"\001");
} else {
PutUser(":"+data["nick"]+" "+data["type"]+" "+data["target"]+" :["+timeStr+"] "+data["message"]);
}
}
for (unsigned int i=0; i<columnCount; i++) {
free(columnData[i]);
}
mysql_free_result(result);
mysql_stmt_close(statement);
if (logLimit==1) {
statement = mysql_stmt_init(database);
status = mysql_stmt_prepare(statement, "DELETE FROM `messages`", 22);
if (status==0) {
mysql_stmt_execute(statement);
mysql_stmt_close(statement);
}
}
PutUser(":*LogMySQL!LogMySQL@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Playback Complete.");
}
private:
MYSQL *database;
bool databaseConnected;
bool connected;
bool replayAll;
unsigned long logLimit;
int logLevel;
};
template<> void TModInfo<CLogMySQL>(CModInfo& Info) {
Info.SetWikiPage("logmysql");
}
NETWORKMODULEDEFS(CLogMySQL, "Add logging to MySQL")

644
logsqlite.cpp Executable file
View File

@ -0,0 +1,644 @@
//
// 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 <znc/Chan.h>
#include <znc/User.h>
#include <znc/IRCNetwork.h>
#include <znc/Modules.h>
#include <time.h>
#include <sqlite3.h>
class CLogSQLite : public CModule {
public:
MODCONSTRUCTOR(CLogSQLite) {}
virtual bool OnLoad(const CString& sArgs, CString& sMessage) {
connected = true;
CString savePath = GetSavePath();
savePath += "/log.sqlite";
bool found = false;
FILE *fp = fopen(savePath.c_str(), "rb");
if (fp!=NULL) {
found = true;
fclose(fp);
}
sqlite3_open(savePath.c_str(), &database);
if (!found) {
sqlite3_stmt *result;
int status = sqlite3_prepare(database, "CREATE TABLE `settings` (`name` text, `value` text)", -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))", -1, &result, NULL);
if (status==SQLITE_OK) {
sqlite3_step(result);
sqlite3_finalize(result);
}
SetSetting("replayAll","0");
SetSetting("logLimit","1");
SetSetting("logLevel","1");
}
replayAll = atoi(GetSetting("replayAll").c_str());
logLimit = strtoul(GetSetting("logLimit").c_str(), NULL, 10);
logLevel = atoi(GetSetting("logLevel").c_str());
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");
}
}
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) {
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 (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);
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);
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);
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;
}
//Server stuff
virtual void OnIRCDisconnected() {
if (connected) {
connected = false;
if (logLevel>=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 vector<CChan*>& vChans) {
if (logLevel>=2) {
vector<CChan*>::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(),"JOIN",sMessage);
}
}
virtual void OnNick(const CNick& OldNick, const CString& sNewNick, const vector<CChan*>& vChans) {
if (logLevel>=2) {
vector<CChan*>::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());
Replay();
}
virtual void OnClientDisconnect() {
if (!m_pNetwork->IsUserAttached()) {
SetSetting("clientDisconnected",GetUNIXTime());
}
}
virtual void OnModCommand(const CString& sCmdLine) {
CString sCommand = sCmdLine.Token(0);
CString sArgs = sCmdLine.Token(1, true);
if (sCommand.Equals("HELP")) {
PutModule("replay - Play back the messages received.");
PutModule("replayAll - Set LogSQLite to replay all messages stored (default is off).");
PutModule("logLimit - 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).");
PutModule("logLevel - 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).");
PutModule("-----");
PutModule("Layout is as below.");
PutModule("Command [arguments]");
PutModule("-----");
PutModule("If you do not include an argument on items that supports arguments, LogSQLite will return the current setting for the item.");
} else if (sCommand.Equals("REPLAY")) {
Replay();
PutModule("Replayed");
} else if (sCommand.Equals("REPLAYALL")) {
if (sArgs.empty()) {
CString status = (replayAll ? "On" : "Off");
PutModule("ReplayAll is set to: "+status);
} else {
if (sArgs.Equals("ON") || sArgs.Equals("1") || sArgs.Equals("true")) {
replayAll = true;
SetSetting("replayAll", "1");
} else {
replayAll = false;
SetSetting("replayAll", "0");
}
CString status = (replayAll ? "On" : "Off");
PutModule("ReplayAll is now set to: "+status);
}
} else if (sCommand.Equals("LOGLIMIT")) {
if (sArgs.empty()) {
CString result;
char limitStr[20];
snprintf(limitStr, sizeof(limitStr), "%lu", logLimit);
result = limitStr;
PutModule("LogLimit is set to: "+result);
} else {
logLimit = strtoul(sArgs.c_str(), NULL, 10);
CString result;
char limitStr[20];
snprintf(limitStr, sizeof(limitStr), "%lu", logLimit);
result = limitStr;
SetSetting("logLimit", result);
PutModule("LogLimit is now set to: "+result);
}
} else if (sCommand.Equals("LOGLEVEL")) {
if (sArgs.empty()) {
CString result;
char levelStr[20];
snprintf(levelStr, sizeof(levelStr), "%d", logLevel);
result = levelStr;
PutModule("LogLevel is set to: "+result);
} else {
logLevel = atoi(sArgs.c_str());
CString result;
char levelStr[20];
snprintf(levelStr, sizeof(levelStr), "%d", logLevel);
result = levelStr;
SetSetting("logLevel", result);
PutModule("LogLevel is now set to: "+result);
}
} else
PutModule("Unknown command ["+sCommand+"] for help, type help.");
}
void Replay() {
PutUser(":*LogSQLite!LogSQLite@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :Buffer Playback...");
CString lastOnline = GetSetting("clientDisconnected");
sqlite3_stmt *result;
int status = SQLITE_OK;
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);
map<int,CString> columns;
for (int i=0; i<columnCount; i++) {
columns[i] = CString(sqlite3_column_name(result, i));
}
while (true) {
status = SQLITE_BUSY;
while (status==SQLITE_BUSY) {
status = sqlite3_step(result);
}
if (status!=SQLITE_ROW)
break;
map<CString,CString> data;
int dataCount = sqlite3_data_count(result);
for (int i=0; i<dataCount; i++) {
data[columns[i]] = CString((const char *)sqlite3_column_text(result, i));
}
time_t now = time(NULL);
time_t unixTime = (time_t)strtol(data["time"].c_str(),NULL,10);
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);
if (data["type"].Equals("DISCONNECT")) {
PutUser(":*LogSQLite!LogSQLite@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :["+timeStr+"] Server Disconnected");
} else if (data["type"].Equals("CONNECT")) {
PutUser(":*LogSQLite!LogSQLite@znc.in NOTICE "+m_pNetwork->GetIRCNick().GetNickMask()+" :["+timeStr+"] Server Connected");
} else if (data["type"].Equals("JOIN")) {
PutUser(":"+data["nick"]+" NOTICE "+data["target"]+" :["+timeStr+"] joined");
} else if (data["type"].Equals("PART")) {
if (data["message"].Equals(""))
PutUser(":"+data["nick"]+" NOTICE "+data["target"]+" :["+timeStr+"] parted");
else
PutUser(":"+data["nick"]+" NOTICE "+data["target"]+" :["+timeStr+"] parted: "+data["message"]);
} else if (data["type"].Equals("TOPIC")) {
PutUser(":"+data["nick"]+" NOTICE "+data["target"]+" :["+timeStr+"] changed topic: "+data["message"]);
} else if (data["type"].Equals("QUIT")) {
if (data["message"].Equals(""))
PutUser(":"+data["nick"]+" NOTICE "+data["target"]+" :["+timeStr+"] quit");
else
PutUser(":"+data["nick"]+" NOTICE "+data["target"]+" :["+timeStr+"] quit: "+data["message"]);
} else if (data["type"].Equals("MODE")) {
PutUser(":"+data["nick"]+" NOTICE "+data["target"]+" :["+timeStr+"] changed mode: "+data["message"]);
} else if (data["type"].Equals("ACTION")) {
PutUser(":"+data["nick"]+" PRIVMSG "+data["target"]+" :\001ACTION ["+timeStr+"] "+data["message"]+"\001");
} else {
PutUser(":"+data["nick"]+" "+data["type"]+" "+data["target"]+" :["+timeStr+"] "+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;
bool connected;
bool replayAll;
unsigned long logLimit;
int logLevel;
};
template<> void TModInfo<CLogSQLite>(CModInfo& Info) {
Info.SetWikiPage("logsqlite");
}
NETWORKMODULEDEFS(CLogSQLite, "Add logging to SQLite")