diff options
49 files changed, 1499 insertions, 736 deletions
diff --git a/data/example.conf b/data/example.conf index 73c6627a3..a746051c0 100644 --- a/data/example.conf +++ b/data/example.conf @@ -498,6 +498,13 @@ options * If set, Services do not allow ownership of nick names, only ownership of accounts. */ nonicknameownership = no + + /* The regex engine to use, as provided by the regex modules. + * Leave commented to disable regex matching. + * + * Note for this to work the regex module providing the regex engine must be loaded. + */ + regexengine = "regex/pcre" } /* diff --git a/data/modules.example.conf b/data/modules.example.conf index 715be1898..101437d04 100644 --- a/data/modules.example.conf +++ b/data/modules.example.conf @@ -311,6 +311,28 @@ sqlite } /* + * m_regex_pcre + * + * Provides the regex engine regex/pcre, which uses the Perl Compatible Regular Expressions library. + */ +module { name = "m_regex_pcre" } + +/* + * m_regex_posix + * + * Provides the regex engine regex/posix, which uses the POSIX compliant regular expressions. + * This is likely the only regex module you will not need extra libraries for. + */ +#module { name = "m_regex_posix" } + +/* + * m_regex_tre + * + * Provides the regex engine regex/tre, which uses the TRE regex library. + */ +#module { name = "m_regex_tre" } + +/* * m_rewrite * * Allows rewriting commands sent to clients. diff --git a/docs/Changes b/docs/Changes index 25b5dc142..7ad5a07da 100644 --- a/docs/Changes +++ b/docs/Changes @@ -1,5 +1,7 @@ Anope Version 1.9.7 -------------------- +A Added ability for using regex as patterns for various commands +A Extended ability of operserv/akill to also match against nick and realname Anope Version 1.9.6 -------------------- diff --git a/docs/Changes.conf b/docs/Changes.conf index cdfbfa094..a9dd31cbf 100644 --- a/docs/Changes.conf +++ b/docs/Changes.conf @@ -1,6 +1,7 @@ Anope Version 1.9.7 ------------------- operserv:notifications removed in favor of log{} blocks +options:regexengine added Anope Version 1.9.6 ------------------- diff --git a/include/anope.h b/include/anope.h index 421d735a3..b286d0fb6 100644 --- a/include/anope.h +++ b/include/anope.h @@ -346,8 +346,9 @@ namespace Anope * @param str The string to check against the pattern (e.g. foobar) * @param mask The pattern to check (e.g. foo*bar) * @param case_sensitive Whether or not the match is case sensitive, default false. + * @param use_regex Whether or not to try regex. case_sensitive is not used in regex. */ - extern CoreExport bool Match(const string &str, const string &mask, bool case_sensitive = false); + extern CoreExport bool Match(const string &str, const string &mask, bool case_sensitive = false, bool use_regex = false); /** Find a message in the message table * @param name The name of the message were looking for diff --git a/include/config.h b/include/config.h index d53b7f41c..1ec67984a 100644 --- a/include/config.h +++ b/include/config.h @@ -456,6 +456,8 @@ class CoreExport ServerConfig bool HidePrivilegedCommands; /* If set, nicks cant be owned/everything is entirely account based */ bool NoNicknameOwnership; + /* Regex engine to use */ + Anope::string RegexEngine; /* A vector of our logfile options */ std::vector<LogInfo *> LogInfos; diff --git a/include/defs.h b/include/defs.h index 0a248daf7..098f48edd 100644 --- a/include/defs.h +++ b/include/defs.h @@ -35,6 +35,7 @@ class Module; class NickAlias; class NickCore; class OperType; +class Regex; class Server; class ServerConfig; class Socket; diff --git a/include/module.h b/include/module.h index 94f565a3a..6865a2c17 100644 --- a/include/module.h +++ b/include/module.h @@ -36,6 +36,7 @@ #include "oper.h" #include "opertype.h" #include "protocol.h" +#include "regexpr.h" #include "regchannel.h" #include "serialize.h" #include "servers.h" diff --git a/include/modules.h b/include/modules.h index 1cb496a54..ce6bbba51 100644 --- a/include/modules.h +++ b/include/modules.h @@ -516,7 +516,7 @@ class CoreExport Module : public Extensible /** Called before a XLine is deleted * @param u The user deleting the XLine - * @param x The XLine, can be NULL for all XLines + * @param x The XLine * @param xlm The xline manager it was deleted from */ virtual void OnDelXLine(User *u, XLine *x, XLineManager *xlm) { } @@ -835,7 +835,6 @@ class CoreExport Module : public Extensible * @param nc The nickcore of the memo being deleted * @param mi The memo info * @param m The memo - * @param number What memo number is being deleted, can be 0 for all memos */ virtual void OnMemoDel(const NickCore *nc, MemoInfo *mi, Memo *m) { } @@ -843,7 +842,6 @@ class CoreExport Module : public Extensible * @param ci The channel of the memo being deleted * @param mi The memo info * @param m The memo - * @param number What memo number is being deleted, can be 0 for all memos */ virtual void OnMemoDel(ChannelInfo *ci, MemoInfo *mi, Memo *m) { } diff --git a/include/oper.h b/include/oper.h index ced1d38dc..b3034b7c5 100644 --- a/include/oper.h +++ b/include/oper.h @@ -14,8 +14,10 @@ class CoreExport XLine : public Serializable { + void InitRegex(); public: Anope::string Mask; + Regex *regex; Anope::string By; time_t Created; time_t Expires; @@ -25,11 +27,18 @@ class CoreExport XLine : public Serializable XLine(const Anope::string &mask, const Anope::string &reason = "", const Anope::string &uid = ""); - XLine(const Anope::string &mask, const Anope::string &by, const time_t expires, const Anope::string &reason, const Anope::string &uid); + XLine(const Anope::string &mask, const Anope::string &by, const time_t expires, const Anope::string &reason, const Anope::string &uid = ""); + ~XLine(); Anope::string GetNick() const; Anope::string GetUser() const; Anope::string GetHost() const; + Anope::string GetReal() const; + + Anope::string GetReason() const; + + bool HasNickOrReal() const; + bool IsRegex() const; Anope::string serialize_name() const; serialized_data serialize(); @@ -41,7 +50,8 @@ class CoreExport XLineManager : public Service char type; /* List of XLines in this XLineManager */ std::vector<XLine *> XLines; - static std::map<Anope::string, XLine *, ci::less> XLinesByUID; + /* Akills can have the same IDs, sometimes */ + static std::multimap<Anope::string, XLine *, ci::less> XLinesByUID; public: /* List of XLine managers we check users against in XLineManager::CheckAll */ static std::list<XLineManager *> XLineManagers; @@ -63,7 +73,7 @@ class CoreExport XLineManager : public Service * Wparam u The user * @return A pair of the XLineManager the user was found in and the XLine they matched, both may be NULL for no match */ - static std::pair<XLineManager *, XLine *> CheckAll(User *u); + static void CheckAll(User *u); /** Generate a unique ID for this XLine * @return A unique ID @@ -116,15 +126,13 @@ class CoreExport XLineManager : public Service void Clear(); /** Checks if a mask can/should be added to the XLineManager + * @param source The source adding the mask. * @param mask The mask * @param expires When the mask would expire - * @return A pair of int and XLine*. - * 1 - Mask already exists - * 2 - Mask already exists, but the expiry time was changed - * 3 - Mask is already covered by another mask - * In each case the XLine it matches/is covered by is returned in XLine* + * @param reason the reason + * @return true if the mask can be added */ - std::pair<int, XLine *> CanAdd(const Anope::string &mask, time_t expires); + bool CanAdd(CommandSource &source, const Anope::string &mask, time_t expires, const Anope::string &reason); /** Checks if this list has an entry * @param mask The mask @@ -134,15 +142,21 @@ class CoreExport XLineManager : public Service /** Check a user against all of the xlines in this XLineManager * @param u The user - * @return The xline the user marches, if any. Also calls OnMatch() + * @return The xline the user marches, if any. + */ + XLine *CheckAllXLines(User *u); + + /** Check a user against an xline + * @param u The user + * @param x The xline */ - virtual XLine *Check(User *u); + virtual bool Check(User *u, XLine *x) = 0; /** Called when a user matches a xline in this XLineManager * @param u The user * @param x The XLine they match */ - virtual void OnMatch(User *u, XLine *x); + virtual void OnMatch(User *u, XLine *x) = 0; /** Called when an XLine expires * @param x The xline diff --git a/include/protocol.h b/include/protocol.h index f1bb9dbbe..edfc5bd53 100644 --- a/include/protocol.h +++ b/include/protocol.h @@ -48,11 +48,11 @@ struct IRCDVar class CoreExport IRCDProto { - private: - virtual void SendSVSKillInternal(const BotInfo *, const User *, const Anope::string &) = 0; - virtual void SendModeInternal(const BotInfo *, const Channel *, const Anope::string &) = 0; + protected: + virtual void SendSVSKillInternal(const BotInfo *, const User *, const Anope::string &); + virtual void SendModeInternal(const BotInfo *, const Channel *, const Anope::string &); virtual void SendModeInternal(const BotInfo *, const User *, const Anope::string &) = 0; - virtual void SendKickInternal(const BotInfo *, const Channel *, const User *, const Anope::string &) = 0; + virtual void SendKickInternal(const BotInfo *, const Channel *, const User *, const Anope::string &); virtual void SendMessageInternal(const BotInfo *bi, const Anope::string &dest, const Anope::string &buf); virtual void SendNoticeInternal(const BotInfo *bi, const Anope::string &dest, const Anope::string &msg); virtual void SendPrivmsgInternal(const BotInfo *bi, const Anope::string &dest, const Anope::string &buf); @@ -60,14 +60,14 @@ class CoreExport IRCDProto virtual void SendPartInternal(const BotInfo *bi, const Channel *chan, const Anope::string &buf); virtual void SendGlobopsInternal(const BotInfo *source, const Anope::string &buf); virtual void SendCTCPInternal(const BotInfo *bi, const Anope::string &dest, const Anope::string &buf); - virtual void SendNumericInternal(const Anope::string &source, int numeric, const Anope::string &dest, const Anope::string &buf); + virtual void SendNumericInternal(int numeric, const Anope::string &dest, const Anope::string &buf); public: virtual ~IRCDProto() { } virtual void SendSVSNOOP(const Server *, bool) { } - virtual void SendTopic(BotInfo *, Channel *) = 0; + virtual void SendTopic(BotInfo *, Channel *); virtual void SendVhostDel(User *) { } - virtual void SendAkill(User *, const XLine *) = 0; + virtual void SendAkill(User *, XLine *) = 0; virtual void SendAkillDel(const XLine *) = 0; virtual void SendSVSKill(const BotInfo *source, const User *user, const char *fmt, ...); virtual void SendMode(const BotInfo *bi, const Channel *dest, const char *fmt, ...); @@ -86,12 +86,12 @@ class CoreExport IRCDProto virtual void SendPong(const Anope::string &servname, const Anope::string &who); virtual void SendJoin(User *, Channel *, const ChannelStatus *) = 0; virtual void SendSQLineDel(const XLine *x) { } - virtual void SendInvite(const BotInfo *bi, const Anope::string &chan, const Anope::string &nick); + virtual void SendInvite(const BotInfo *bi, const Channel *c, const User *u); virtual void SendPart(const BotInfo *bi, const Channel *chan, const char *fmt, ...); virtual void SendGlobops(const BotInfo *source, const char *fmt, ...); virtual void SendSQLine(User *, const XLine *x) { } virtual void SendSquit(Server *, const Anope::string &message); - virtual void SendSVSO(const Anope::string &, const Anope::string &, const Anope::string &) { } + virtual void SendSVSO(const BotInfo *, const Anope::string &, const Anope::string &) { } virtual void SendChangeBotNick(const BotInfo *bi, const Anope::string &newnick); virtual void SendForceNickChange(const User *u, const Anope::string &newnick, time_t when); virtual void SendVhost(User *, const Anope::string &, const Anope::string &) { } @@ -103,14 +103,14 @@ class CoreExport IRCDProto virtual void SendSZLine(User *u, const XLine *) { } virtual void SendSGLine(User *, const XLine *) { } virtual void SendCTCP(const BotInfo *bi, const Anope::string &dest, const char *fmt, ...); - virtual void SendSVSJoin(const Anope::string &, const Anope::string &, const Anope::string &, const Anope::string &) { } - virtual void SendSWhois(const Anope::string &, const Anope::string &, const Anope::string &) { } + virtual void SendSVSJoin(const BotInfo *bi, const Anope::string &, const Anope::string &, const Anope::string &) { } + virtual void SendSWhois(const BotInfo *bi, const Anope::string &, const Anope::string &) { } virtual void SendBOB() { } virtual void SendEOB() { } virtual void SendServer(const Server *) = 0; virtual bool IsNickValid(const Anope::string &) { return true; } virtual bool IsChannelValid(const Anope::string &); - virtual void SendNumeric(const Anope::string &source, int numeric, const Anope::string &dest, const char *fmt, ...); + virtual void SendNumeric(int numeric, const Anope::string &dest, const char *fmt, ...); virtual void SendLogin(User *u) = 0; virtual void SendLogout(User *u) = 0; diff --git a/include/regexpr.h b/include/regexpr.h new file mode 100644 index 000000000..a333c7e1b --- /dev/null +++ b/include/regexpr.h @@ -0,0 +1,47 @@ +/* + * + * (C) 2003-2012 Anope Team + * Contact us at team@anope.org + * + * Please read COPYING and README for further details. + * + * Based on the original code of Epona by Lara. + * Based on the original code of Services by Andy Church. + * + */ + +#ifndef REGEXPR_H +#define REGEXPR_H + +#include "services.h" +#include "anope.h" +#include "service.h" + +class RegexException : public CoreException +{ + public: + RegexException(const Anope::string &reason = "") : CoreException(reason) { } + + virtual ~RegexException() throw() { } +}; + +class CoreExport Regex +{ + Anope::string expression; + protected: + Regex(const Anope::string &expr) : expression(expr) { } + public: + virtual ~Regex() { } + const Anope::string &GetExpression() { return expression; } + virtual bool Matches(const Anope::string &str) = 0; +}; + +class CoreExport RegexProvider : public Service +{ + public: + RegexProvider(Module *o, const Anope::string &n) : Service(o, "Regex", n) { } + virtual Regex *Compile(const Anope::string &) = 0; +}; + +#endif // REGEXPR_H + diff --git a/include/uplink.h b/include/uplink.h index 7f200d426..70b20a28b 100644 --- a/include/uplink.h +++ b/include/uplink.h @@ -25,11 +25,15 @@ class UplinkSocket : public ConnectionSocket, public BufferedSocket class CoreExport Message { - Anope::string source; + private: + const Server *server; + const User *user; std::stringstream buffer; + public: Message(); - Message(const Anope::string &); + explicit Message(const Server *); + explicit Message(const User *); ~Message(); template<typename T> Message &operator<<(const T &val) { diff --git a/modules/commands/cs_akick.cpp b/modules/commands/cs_akick.cpp index 328a169d4..beaa8ffd3 100644 --- a/modules/commands/cs_akick.cpp +++ b/modules/commands/cs_akick.cpp @@ -207,6 +207,8 @@ class CommandCSAKick : public Command if (!Number || Number > ci->GetAkickCount()) return; + FOREACH_MOD(I_OnAkickDel, OnAkickDel(source.u, ci, ci->GetAkick(Number - 1))); + ++Deleted; ci->EraseAkick(Number - 1); } @@ -236,6 +238,8 @@ class CommandCSAKick : public Command bool override = !ci->AccessFor(u).HasPriv("AKICK"); Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "to delete " << mask; + FOREACH_MOD(I_OnAkickDel, OnAkickDel(u, ci, ci->GetAkick(i))); + ci->EraseAkick(i); source.Reply(_("\002%s\002 deleted from %s autokick list."), mask.c_str(), ci->name.c_str()); diff --git a/modules/commands/cs_invite.cpp b/modules/commands/cs_invite.cpp index b38828c9d..be3132907 100644 --- a/modules/commands/cs_invite.cpp +++ b/modules/commands/cs_invite.cpp @@ -71,7 +71,7 @@ class CommandCSInvite : public Command { bool override = !ci->AccessFor(u).HasPriv("INVITE"); - ircdproto->SendInvite(ci->WhoSends(), chan, u2->nick); + ircdproto->SendInvite(ci->WhoSends(), c, u2); if (u2 != u) { source.Reply(_("\002%s\002 has been invited to \002%s\002."), u2->nick.c_str(), c->name.c_str()); diff --git a/modules/commands/cs_list.cpp b/modules/commands/cs_list.cpp index 57c14d157..77d7613fe 100644 --- a/modules/commands/cs_list.cpp +++ b/modules/commands/cs_list.cpp @@ -87,7 +87,7 @@ class CommandCSList : public Command else if (channoexpire && !ci->HasFlag(CI_NO_EXPIRE)) continue; - if (pattern.equals_ci(ci->name) || ci->name.equals_ci(spattern) || Anope::Match(ci->name, pattern) || Anope::Match(ci->name, spattern)) + if (pattern.equals_ci(ci->name) || ci->name.equals_ci(spattern) || Anope::Match(ci->name, pattern, false, true) || Anope::Match(ci->name, spattern, false, true)) { if (((count + 1 >= from && count + 1 <= to) || (!from && !to)) && ++nchans <= Config->CSListMax) { @@ -125,6 +125,10 @@ class CommandCSList : public Command "(Channels with the \002PRIVATE\002 option set are not listed.)\n" "Note that a preceding '#' specifies a range, channel names\n" "are to be written without '#'.")); + if (!Config->RegexEngine.empty()) + source.Reply(" \n" + "Regex matches are also supported using the %s engine.\n" + "Enclose your pattern in // if this desired.", Config->RegexEngine.c_str()); return true; } }; diff --git a/modules/commands/ms_del.cpp b/modules/commands/ms_del.cpp index f0b65eeac..65969b025 100644 --- a/modules/commands/ms_del.cpp +++ b/modules/commands/ms_del.cpp @@ -107,13 +107,15 @@ class CommandMSDel : public Command } else { - if (ci) - FOREACH_MOD(I_OnMemoDel, OnMemoDel(ci, mi, NULL)); - else - FOREACH_MOD(I_OnMemoDel, OnMemoDel(u->Account(), mi, NULL)); /* Delete all memos. */ for (unsigned i = 0, end = mi->memos.size(); i < end; ++i) + { + if (ci) + FOREACH_MOD(I_OnMemoDel, OnMemoDel(ci, mi, mi->memos[i])); + else + FOREACH_MOD(I_OnMemoDel, OnMemoDel(u->Account(), mi, mi->memos[i])); delete mi->memos[i]; + } mi->memos.clear(); if (!chan.empty()) source.Reply(_("All memos for channel %s have been deleted."), chan.c_str()); diff --git a/modules/commands/ns_ajoin.cpp b/modules/commands/ns_ajoin.cpp index 5a5dbe7f0..03b710356 100644 --- a/modules/commands/ns_ajoin.cpp +++ b/modules/commands/ns_ajoin.cpp @@ -194,8 +194,9 @@ class NSAJoin : public Module void OnNickIdentify(User *u) anope_override { AJoinList *channels = u->Account()->GetExt<AJoinList *>("ns_ajoin_channels"); + BotInfo *bi = findbot(Config->NickServ); - if (channels == NULL) + if (channels == NULL || bi == NULL) return; for (unsigned i = 0; i < channels->size(); ++i) @@ -255,15 +256,14 @@ class NSAJoin : public Module } } - if (need_invite) + if (need_invite && c != NULL) { - BotInfo *bi = findbot(Config->NickServ); - if (!bi || !ci->AccessFor(u).HasPriv("INVITE")) + if (!ci->AccessFor(u).HasPriv("INVITE")) continue; - ircdproto->SendInvite(bi, channels->at(i).first, u->nick); + ircdproto->SendInvite(bi, c, u); } - ircdproto->SendSVSJoin(Config->NickServ, u->nick, channels->at(i).first, key); + ircdproto->SendSVSJoin(bi, u->nick, channels->at(i).first, key); } } }; diff --git a/modules/commands/ns_list.cpp b/modules/commands/ns_list.cpp index 491ed8733..de2852302 100644 --- a/modules/commands/ns_list.cpp +++ b/modules/commands/ns_list.cpp @@ -93,7 +93,7 @@ class CommandNSList : public Command * Instead we build a nice nick!user@host buffer to compare. * The output is then generated separately. -TheShadow */ Anope::string buf = Anope::printf("%s!%s", na->nick.c_str(), !na->last_usermask.empty() ? na->last_usermask.c_str() : "*@*"); - if (na->nick.equals_ci(pattern) || Anope::Match(buf, pattern)) + if (na->nick.equals_ci(pattern) || Anope::Match(buf, pattern, false, true)) { if (((count + 1 >= from && count + 1 <= to) || (!from && !to)) && ++nnicks <= Config->NSListMax) { @@ -156,6 +156,10 @@ class CommandNSList : public Command " \n" " \002LIST * NOEXPIRE\002\n" " Lists all registered nicks which have been set to not expire.\n")); + if (!Config->RegexEngine.empty()) + source.Reply(" \n" + "Regex matches are also supported using the %s engine.\n" + "Enclose your pattern in // if this desired.", Config->RegexEngine.c_str()); return true; } diff --git a/modules/commands/os_akill.cpp b/modules/commands/os_akill.cpp index 89485f706..f59c26e1f 100644 --- a/modules/commands/os_akill.cpp +++ b/modules/commands/os_akill.cpp @@ -60,19 +60,24 @@ class CommandOSAKill : public Command void DoAdd(CommandSource &source, const std::vector<Anope::string> ¶ms) { User *u = source.u; - unsigned last_param = 2; Anope::string expiry, mask; - time_t expires; - mask = params.size() > 1 ? params[1] : ""; - if (!mask.empty() && mask[0] == '+') + if (params.size() < 2) + { + this->OnSyntaxError(source, "ADD"); + return; + } + + spacesepstream sep(params[1]); + sep.GetToken(mask); + + if (mask[0] == '+') { expiry = mask; - mask = params.size() > 2 ? params[2] : ""; - last_param = 3; + sep.GetToken(mask); } - expires = !expiry.empty() ? dotime(expiry) : Config->AutokillExpiry; + time_t expires = !expiry.empty() ? dotime(expiry) : Config->AutokillExpiry; /* If the expiry given does not contain a final letter, it's in days, * said the doc. Ah well. */ @@ -87,84 +92,108 @@ class CommandOSAKill : public Command else if (expires > 0) expires += Anope::CurTime; - if (params.size() <= last_param) + if (sep.StreamEnd()) { this->OnSyntaxError(source, "ADD"); return; } - Anope::string reason = params[last_param]; - if (last_param == 2 && params.size() > 3) - reason += " " + params[3]; - if (!mask.empty() && !reason.empty()) + Anope::string reason; + if (mask.find('#') != Anope::string::npos) { - User *targ = NULL; - std::pair<int, XLine *> canAdd = akills->CanAdd(mask, expires); - if (mask.find('!') != Anope::string::npos) - source.Reply(_("\002Reminder\002: AKILL masks cannot contain nicknames; make sure you have \002not\002 included a nick portion in your mask.")); - else if (mask.find('@') == Anope::string::npos && !(targ = finduser(mask))) - source.Reply(BAD_USERHOST_MASK); - else if (mask.find_first_not_of("~@.*?") == Anope::string::npos) - source.Reply(USERHOST_MASK_TOO_WIDE, mask.c_str()); - else if (canAdd.first == 1) - source.Reply(_("\002%s\002 already exists on the AKILL list."), canAdd.second->Mask.c_str()); - else if (canAdd.first == 2) - source.Reply(_("Expiry time of \002%s\002 changed."), canAdd.second->Mask.c_str()); - else if (canAdd.first == 3) - source.Reply(_("\002%s\002 is already covered by %s."), mask.c_str(), canAdd.second->Mask.c_str()); - else + Anope::string remaining = sep.Ge |
