summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/u_listmode.h72
-rw-r--r--src/modules/extra/m_pgsql.cpp161
-rw-r--r--src/modules/extra/m_sqlv2.h27
-rw-r--r--src/modules/m_chanfilter.cpp181
4 files changed, 238 insertions, 203 deletions
diff --git a/include/u_listmode.h b/include/u_listmode.h
index f7d74a2c7..d30da1e5e 100644
--- a/include/u_listmode.h
+++ b/include/u_listmode.h
@@ -126,7 +126,7 @@ class ListModeBase : public ModeHandler
{
// Make one
el = new modelist;
- channel->Extend(infokey, (char*)el);
+ channel->Extend(infokey, el);
}
// Clean the mask up
@@ -137,8 +137,10 @@ class ListModeBase : public ModeHandler
{
if(parameter == it->mask)
{
+ /* Give a subclass a chance to error about this */
+ TellAlreadyOnList(source, channel, parameter);
+
// it does, deny the change
- parameter = "";
return MODEACTION_DENY;
}
}
@@ -153,22 +155,44 @@ class ListModeBase : public ModeHandler
maxsize = el->size();
if (maxsize < it->limit)
{
- // And now add the mask onto the list...
- ListItem e;
- e.mask = parameter;
- e.nick = source->nick;
- e.time = stringtime();
+ /* Ok, it *could* be allowed, now give someone subclassing us
+ * a chance to validate the parameter.
+ * The param is passed by reference, so they can both modify it
+ * and tell us if we allow it or not.
+ *
+ * eg, the subclass could:
+ * 1) allow
+ * 2) 'fix' parameter and then allow
+ * 3) deny
+ */
+ if(ValidateParam(source, channel, parameter))
+ {
+ // And now add the mask onto the list...
+ ListItem e;
+ e.mask = parameter;
+ e.nick = source->nick;
+ e.time = stringtime();
- el->push_back(e);
- return MODEACTION_ALLOW;
+ el->push_back(e);
+ return MODEACTION_ALLOW;
+ }
+ else
+ {
+ /* If they deny it they have the job of giving an error message */
+ return MODEACTION_DENY;
+ }
}
}
}
- // List is full
- WriteServ(source->fd, "478 %s %s %s :Channel ban/ignore list is full", source->nick, channel->name, parameter.c_str());
+ /* List is full, give subclass a chance to send a custom message */
+ if(!TellListTooLong(source, channel, parameter))
+ {
+ WriteServ(source->fd, "478 %s %s %s :Channel ban/ignore list is full", source->nick, channel->name, parameter.c_str());
+ }
+
parameter = "";
- return MODEACTION_DENY;
+ return MODEACTION_DENY;
}
else
{
@@ -187,6 +211,11 @@ class ListModeBase : public ModeHandler
}
return MODEACTION_ALLOW;
}
+ else
+ {
+ /* Tried to remove something that wasn't set */
+ TellNotSet(source, channel, parameter);
+ }
}
parameter = "";
return MODEACTION_DENY;
@@ -244,6 +273,25 @@ class ListModeBase : public ModeHandler
}
}
}
+
+ virtual bool ValidateParam(userrec* source, chanrec* channel, std::string &parameter)
+ {
+ return true;
+ }
+
+ virtual bool TellListTooLong(userrec* source, chanrec* channel, std::string &parameter)
+ {
+ return false;
+ }
+
+ virtual void TellAlreadyOnList(userrec* source, chanrec* channel, std::string &parameter)
+ {
+ }
+
+ virtual void TellNotSet(userrec* source, chanrec* channel, std::string &parameter)
+ {
+
+ }
};
#endif
diff --git a/src/modules/extra/m_pgsql.cpp b/src/modules/extra/m_pgsql.cpp
index c44c66bc8..14e32ac36 100644
--- a/src/modules/extra/m_pgsql.cpp
+++ b/src/modules/extra/m_pgsql.cpp
@@ -57,14 +57,6 @@ typedef std::map<std::string, SQLConn*> ConnMap;
*/
enum SQLstatus { CREAD, CWRITE, WREAD, WWRITE };
-inline std::string pop_front_r(std::deque<std::string> &d)
-{
- std::string r = d.front();
- d.pop_front();
- return r;
-
-}
-
/** QueryQueue, a queue of queries waiting to be executed.
* This maintains two queues internally, one for 'priority'
* queries and one for less important ones. Each queue has
@@ -74,21 +66,38 @@ inline std::string pop_front_r(std::deque<std::string> &d)
* queries in the priority queue they will be executed first,
* 'unimportant' queries will only be executed when the
* priority queue is empty.
+ *
+ * These are lists of SQLresult so we can, from the moment the
+ * SQLrequest is recieved, be beginning to construct the result
+ * object. The copy in the deque can then be submitted in-situ
+ * and finally deleted from this queue. No copies of the SQLresult :)
+ *
+ * Because we work on the SQLresult in-situ, we need a way of accessing the
+ * result we are currently processing, QueryQueue::front(), but that call
+ * needs to always return the same element until that element is removed
+ * from the queue, this is what the 'which' variable is. New queries are
+ * always added to the back of one of the two queues, but if when front()
+ * is first called then the priority queue is empty then front() will return
+ * a query from the normal queue, but if a query is then added to the priority
+ * queue then front() must continue to return the front of the *normal* queue
+ * until pop() is called.
*/
class QueryQueue
{
private:
- std::deque<std::string> priority; /* The priority queue */
- std::deque<std::string> normal; /* The 'normal' queue */
+ std::deque<SQLresult> priority; /* The priority queue */
+ std::deque<SQLresult> normal; /* The 'normal' queue */
+ enum { PRI, NOR, NON } which; /* Which queue the currently active element is at the front of */
public:
QueryQueue()
+ : which(NON)
{
}
- void push_back(const std::string &q, bool pri = false)
+ void push(const Query &q, bool pri = false)
{
log(DEBUG, "QueryQueue::push_back(): Adding %s query to queue: %s", ((pri) ? "priority" : "non-priority"), q.c_str());
@@ -98,21 +107,47 @@ public:
normal.push_back(q);
}
- inline std::string pop_front()
+ void pop()
{
- std::string res;
-
- if(priority.size())
+ if((which == PRI) && priority.size())
{
- return pop_front_r(priority);
+ priority.pop_front();
}
- else if(normal.size())
+ else if((which == NOR) && normal.size())
{
- return pop_front_r(normal);
+ normal.pop_front();
}
- else
+
+ /* Silently do nothing if there was no element to pop() */
+ }
+
+ SQLresult& front()
+ {
+ switch(which)
{
- return "";
+ case PRI:
+ return priority.front();
+ case NOR:
+ return normal.front();
+ default:
+ if(priority.size())
+ {
+ which = PRI;
+ return priority.front();
+ }
+
+ if(normal.size())
+ {
+ which = NOR;
+ return normal.front();
+ }
+
+ /* This will probably result in a segfault,
+ * but the caller should have checked totalsize()
+ * first so..meh - moron :p
+ */
+
+ return priority.front();
}
}
@@ -120,6 +155,11 @@ public:
{
return std::make_pair(priority.size(), normal.size());
}
+
+ int totalsize()
+ {
+ return priority.size() + normal.size();
+ }
};
/** SQLConn represents one SQL session.
@@ -141,14 +181,16 @@ private:
bool ssl; /* If we should require SSL */
PGconn* sql; /* PgSQL database connection handle */
SQLstatus status; /* PgSQL database connection status */
+ bool qinprog;/* If there is currently a query in progress */
+ QueryQueue queue; /* Queue of queries waiting to be executed on this connection */
+ Query query; /* The currently active query on this connection */
public:
- QueryQueue queue; /* Queue of queries waiting to be executed on this connection */
/* This class should only ever be created inside this module, using this constructor, so we don't have to worry about the default ones */
SQLConn(Server* srv, const std::string &h, unsigned int p, const std::string &d, const std::string &u, const std::string &pwd, bool s)
- : InspSocket::InspSocket(), Srv(srv), dbhost(h), dbport(p), dbname(d), dbuser(u), dbpass(pwd), ssl(s), sql(NULL), status(CWRITE)
+ : InspSocket::InspSocket(), Srv(srv), dbhost(h), dbport(p), dbname(d), dbuser(u), dbpass(pwd), ssl(s), sql(NULL), status(CWRITE), qinprog(false)
{
log(DEBUG, "Creating new PgSQL connection to database %s on %s:%u (%s/%s)", dbname.c_str(), dbhost.c_str(), dbport, dbuser.c_str(), dbpass.c_str());
@@ -191,7 +233,7 @@ public:
~SQLConn()
{
-
+ Close();
}
bool DoResolve()
@@ -280,11 +322,14 @@ public:
virtual void Close()
{
+ log(DEBUG,"SQLConn::Close");
+
+ if(this->fd > 01)
+ socket_ref[this->fd] = NULL;
this->fd = -1;
this->state = I_ERROR;
this->OnError(I_ERR_SOCKET);
this->ClosePending = true;
- log(DEBUG,"SQLConn::Close");
if(sql)
{
@@ -303,20 +348,18 @@ public:
log(DEBUG, "PGconnectPoll: PGRES_POLLING_WRITING");
WantWrite();
status = CWRITE;
- DoPoll();
- break;
+ return DoPoll();
case PGRES_POLLING_READING:
log(DEBUG, "PGconnectPoll: PGRES_POLLING_READING");
status = CREAD;
break;
case PGRES_POLLING_FAILED:
log(DEBUG, "PGconnectPoll: PGRES_POLLING_FAILED: %s", PQerrorMessage(sql));
- Close();
return false;
case PGRES_POLLING_OK:
log(DEBUG, "PGconnectPoll: PGRES_POLLING_OK");
status = WWRITE;
- break;
+ return DoConnectedPoll()
default:
log(DEBUG, "PGconnectPoll: wtf?");
break;
@@ -325,8 +368,15 @@ public:
return true;
}
- bool ProcessData()
+ bool DoConnectedPoll()
{
+ if(!qinprog && queue.totalsize())
+ {
+ /* There's no query currently in progress, and there's queries in the queue. */
+ query = queue.pop_front();
+ DoQuery();
+ }
+
if(PQconsumeInput(sql))
{
log(DEBUG, "PQconsumeInput succeeded");
@@ -353,6 +403,8 @@ public:
PQclear(result);
}
+
+ qinprog = false;
}
return true;
@@ -420,13 +472,15 @@ public:
bool DoEvent()
{
+ bool ret;
+
if((status == CREAD) || (status == CWRITE))
{
- DoPoll();
+ ret = DoPoll();
}
else
{
- ProcessData();
+ ret = DoConnectedPoll();
}
switch(PQflush(sql))
@@ -443,7 +497,7 @@ public:
break;
}
- return true;
+ return ret;
}
std::string MkInfoStr()
@@ -482,39 +536,30 @@ public:
return "Err...what, erm..BUG!";
}
- bool Query(const std::string &query)
+ SQLerror Query(const Query &query, bool pri)
{
+ queue.push_back(query, pri);
+
if((status == WREAD) || (status == WWRITE))
{
- if(PQsendQuery(sql, query.c_str()))
+ if(!qinprog)
{
- log(DEBUG, "Dispatched query: %s", query.c_str());
- return true;
- }
- else
- {
- log(DEBUG, "Failed to dispatch query: %s", PQerrorMessage(sql));
- return false;
+ if(PQsendQuery(sql, query.c_str()))
+ {
+ log(DEBUG, "Dispatched query: %s", query.c_str());
+ qinprog = true;
+ return SQLerror();
+ }
+ else
+ {
+ log(DEBUG, "Failed to dispatch query: %s", PQerrorMessage(sql));
+ return SQLerror(QSEND_FAIL, PQerrorMessage(sql));
+ }
}
}
log(DEBUG, "Can't query until connection is complete");
- return false;
- }
-
- virtual void OnClose()
- {
- /* Close PgSQL connection */
- }
-
- virtual void OnError(InspSocketError e)
- {
- /* Unsure if we need this, we should be reading/writing via the PgSQL API rather than the insp one... */
- }
-
- virtual void OnTimeout()
- {
- /* Unused, I think */
+ return SQLerror(BAD_CONN, "Can't query until connection is complete");
}
};
@@ -584,7 +629,7 @@ public:
if((iter = connections.find(req->dbid)) != connections.end())
{
/* Execute query */
- iter->second->queue.push_back(req->query, req->pri);
+ req->error = iter->second->Query(Query(req->query, req->GetSource(), this), req->pri);
return SQLSUCCESS;
}
diff --git a/src/modules/extra/m_sqlv2.h b/src/modules/extra/m_sqlv2.h
index 521a95640..5faa76cc2 100644
--- a/src/modules/extra/m_sqlv2.h
+++ b/src/modules/extra/m_sqlv2.h
@@ -8,19 +8,15 @@
#include <string>
#include "modules.h"
-enum SQLerrorNum { BAD_DBID };
+enum SQLerrorNum { NO_ERROR, BAD_DBID, BAD_CONN, QSEND_FAIL };
class SQLerror
{
SQLerrorNum id;
+ std::string str;
public:
-
- SQLerror()
- {
- }
-
- SQLerror(SQLerrorNum i)
- : id(i)
+ SQLerror(SQLerrorNum i = NO_ERROR, const std::string &s = "")
+ : id(i), str(s)
{
}
@@ -29,12 +25,26 @@ public:
id = i;
}
+ void Str(const std::string &s)
+ {
+ str = s;
+ }
+
const char* Str()
{
+ if(str.length())
+ return str.c_str();
+
switch(id)
{
+ case NO_ERROR:
+ return "No error";
case BAD_DBID:
return "Invalid database ID";
+ case BAD_CONN:
+ return "Invalid connection";
+ case QSEND_FAIL:
+ return "Sending query failed";
default:
return "Unknown error";
}
@@ -57,6 +67,7 @@ public:
class SQLresult : public Request
{
+
public:
SQLresult(Module* s, Module* d)
: Request(SQLRESID, s, d)
diff --git a/src/modules/m_chanfilter.cpp b/src/modules/m_chanfilter.cpp
index b9fe2225f..cfb1d351e 100644
--- a/src/modules/m_chanfilter.cpp
+++ b/src/modules/m_chanfilter.cpp
@@ -24,33 +24,60 @@ using namespace std;
#include "modules.h"
#include "helperfuncs.h"
#include "hashcomp.h"
+#include "u_listmode.h"
/* $ModDesc: Provides channel-specific censor lists (like mode +G but varies from channel to channel) */
-typedef std::vector<irc::string> SpamList;
+class ChanFilter : public ListModeBase
+{
+ public:
+ ChanFilter(Server* serv) : ListModeBase(serv, 'g', "End of channel spamfilter list", "941", "940", "chanfilter") { }
+
+ virtual bool ValidateParam(userrec* user, chanrec* chan, std::string &word)
+ {
+ if (word.length() > 35)
+ {
+ WriteServ(user->fd, "935 %s %s %s :word is too long for censor list",user->nick, chan->name,word.c_str());
+ return false;
+ }
+
+ return true;
+ }
+
+ virtual bool TellListTooLong(userrec* user, chanrec* chan, std::string &word)
+ {
+ WriteServ(user->fd,"939 %s %s %s :Channel spamfilter list is full",user->nick, chan->name, word.c_str());
+ return true;
+ }
+
+ virtual void TellAlreadyOnList(userrec* user, chanrec* chan, std::string &word)
+ {
+ WriteServ(user->fd,"937 %s %s :The word %s is already on the spamfilter list",user->nick, chan->name,word.c_str());
+ }
+
+ virtual void TellNotSet(userrec* user, chanrec* chan, std::string &word)
+ {
+ WriteServ(user->fd,"938 %s %s :No such spamfilter word is set",user->nick, chan->name);
+ }
+};
class ModuleChanFilter : public Module
{
Server *Srv;
- ConfigReader *Conf;
- long MaxEntries;
+ ChanFilter* cf;
public:
ModuleChanFilter(Server* Me)
- : Module::Module(Me)
+ : Module::Module(Me), Srv(Me)
{
- Srv = Me;
- Conf = new ConfigReader;
- Srv->AddExtendedListMode('g');
- MaxEntries = Conf->ReadInteger("chanfilter","maxsize",0,true);
- if (MaxEntries == 0)
- MaxEntries = 32;
+ cf = new ChanFilter(Srv);
+ Srv->AddMode(cf, 'g');
}
void Implements(char* List)
{
- List[I_On005Numeric] = List[I_OnUserPart] = List[I_OnRehash] = List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = List[I_OnExtendedMode] = List[I_OnSendList] = List[I_OnSyncChannel] = 1;
+ List[I_OnCleanup] = List[I_On005Numeric] = List[I_OnChannelDelete] = List[I_OnRehash] = List[I_OnUserPreMessage] = List[I_OnUserPreNotice] = List[I_OnSyncChannel] = 1;
}
virtual void On005Numeric(std::string &output)
@@ -58,42 +85,30 @@ class ModuleChanFilter : public Module
InsertMode(output,"g",1);
}
- virtual void OnUserPart(userrec* user, chanrec* channel, const std::string &partreason)
+ virtual void OnChannelDelete(chanrec* chan)
{
- // when the last user parts, delete the list
- if (Srv->CountUsers(channel) == 1)
- {
- SpamList* spamlist = (SpamList*)channel->GetExt("spam_list");
- if (spamlist)
- {
- channel->Shrink("spam_list");
- DELETE(spamlist);
- }
- }
+ cf->DoChannelDelete(chan);
}
virtual void OnRehash(const std::string &parameter)
{
- DELETE(Conf);
- Conf = new ConfigReader;
- // re-read our config options on a rehash
- MaxEntries = Conf->ReadInteger("chanfilter","maxsize",0,true);
+ cf->DoRehash();
}
virtual int ProcessMessages(userrec* user,chanrec* chan,std::string &text)
{
-
// Create a copy of the string in irc::string
irc::string line = text.c_str();
- SpamList* spamlist = (SpamList*)chan->GetExt("spam_list");
- if (spamlist)
+ modelist* list = (modelist*)chan->GetExt(cf->GetInfoKey());
+
+ if (list)
{
- for (SpamList::iterator i = spamlist->begin(); i != spamlist->end(); i++)
+ for (modelist::iterator i = list->begin(); i != list->end(); i++)
{
- if (line.find(*i) != std::string::npos)
+ if (line.find(i->mask.c_str()) != std::string::npos)
{
- WriteServ(user->fd,"936 %s %s %s :Your message contained a censored word, and was blocked",user->nick, chan->name, i->c_str());
+ WriteServ(user->fd,"936 %s %s %s :Your message contained a censored word, and was blocked",user->nick, chan->name, i->mask.c_str());
return 1;
}
}
@@ -110,112 +125,29 @@ class ModuleChanFilter : public Module
else return 0;
}
- virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status)
+ virtual void OnCleanup(int target_type, void* item)
{
- return OnUserPreMessage(user,dest,target_type,text,status);
+ cf->DoCleanup(target_type, item);
}
- virtual int OnExtendedMode(userrec* user, void* target, char modechar, int type, bool mode_on, string_list &params)
- {
- if ((modechar == 'g') && (type == MT_CHANNEL))
- {
- chanrec* chan = (chanrec*)target;
-
- irc::string word = params[0].c_str();
-
- if (word == "")
- return -1;
-
- if (mode_on)
- {
- SpamList* spamlist = (SpamList*)chan->GetExt("spam_list");
- if (!spamlist)
- {
- spamlist = new SpamList;
- chan->Extend("spam_list",(char*)spamlist);
- }
- if (spamlist->size() < (unsigned)MaxEntries)
- {
- if (word.length() > 35)
- {
- WriteServ(user->fd,"935 %s %s %s :word is too long for censor list",user->nick, chan->name,word.c_str());
- return -1;
- }
- for (SpamList::iterator i = spamlist->begin(); i != spamlist->end(); i++)
- {
- if (*i == word)
- {
- WriteServ(user->fd,"937 %s %s :The word %s is already on the spamfilter list",user->nick, chan->name,word.c_str());
- return -1;
- }
- }
- spamlist->push_back(word);
- return 1;
- }
- WriteServ(user->fd,"939 %s %s :Channel spamfilter list is full",user->nick, chan->name);
- return -1;
- }
- else
- {
- SpamList* spamlist = (SpamList*)chan->GetExt("spam_list");
- if (spamlist)
- {
- for (SpamList::iterator i = spamlist->begin(); i != spamlist->end(); i++)
- {
- if (*i == word)
- {
- spamlist->erase(i);
- return 1;
- }
- }
- }
- WriteServ(user->fd,"938 %s %s :No such spamfilter word is set",user->nick, chan->name);
- return -1;
- }
- return -1;
- }
- return 0;
- }
-
- virtual void OnSendList(userrec* user, chanrec* channel, char mode)
+ virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status)
{
- if (mode == 'g')
- {
- SpamList* spamlist = (SpamList*)channel->GetExt("spam_list");
- if (spamlist)
- {
- for (SpamList::iterator i = spamlist->begin(); i != spamlist->end(); i++)
- {
- WriteServ(user->fd,"941 %s %s %s",user->nick, channel->name,i->c_str());
- }
- }
- WriteServ(user->fd,"940 %s %s :End of channel spamfilter list",user->nick, channel->name);
- }
+ return OnUserPreMessage(user,dest,target_type,text,status);
}
- virtual ~ModuleChanFilter()
+ virtual void OnSyncChannel(chanrec* chan, Module* proto, void* opaque)
{
- DELETE(Conf);
+ cf->DoSyncChannel(chan, proto, opaque);
}
-
+
virtual Version GetVersion()
{
- return Version(1,0,0,0,VF_STATIC|VF_VENDOR);
+ return Version(1,0,0,1,VF_STATIC|VF_VENDOR);
}
- virtual void OnSyncChannel(chanrec* chan, Module* proto, void* opaque)
+ virtual ~ModuleChanFilter()
{
- SpamList* spamlist = (SpamList*)chan->GetExt("spam_list");
- string_list commands;
- if (spamlist)
- {
- for (SpamList::iterator i = spamlist->begin(); i != spamlist->end(); i++)
- {
- proto->ProtoSendMode(opaque,TYPE_CHANNEL,chan,"+g "+std::string(i->c_str()));
- }
- }
}
-
};
@@ -242,4 +174,3 @@ extern "C" void * init_module( void )
{
return new ModuleChanFilterFactory;
}
-