summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/example.conf2
-rw-r--r--modules/commands/cs_entrymsg.cpp2
-rw-r--r--modules/commands/cs_flags.cpp2
-rw-r--r--modules/commands/cs_list.cpp6
-rw-r--r--modules/commands/ns_access.cpp2
-rw-r--r--modules/commands/ns_ajoin.cpp2
-rw-r--r--modules/commands/ns_alist.cpp2
-rw-r--r--modules/commands/ns_cert.cpp2
-rw-r--r--modules/commands/ns_info.cpp6
-rw-r--r--modules/commands/ns_list.cpp6
-rw-r--r--modules/commands/ns_set.cpp12
-rw-r--r--modules/commands/os_akill.cpp7
-rw-r--r--modules/commands/os_dns.cpp36
-rw-r--r--modules/commands/os_forbid.cpp6
-rw-r--r--modules/commands/os_ignore.cpp10
-rw-r--r--modules/commands/os_info.cpp9
-rw-r--r--modules/commands/os_news.cpp10
-rw-r--r--modules/commands/os_oper.cpp6
-rw-r--r--modules/commands/os_set.cpp9
-rw-r--r--modules/commands/os_sxline.cpp20
20 files changed, 128 insertions, 29 deletions
diff --git a/data/example.conf b/data/example.conf
index d102875d7..c038d22f6 100644
--- a/data/example.conf
+++ b/data/example.conf
@@ -793,7 +793,7 @@ log
* memoserv/info - Can see any information with /memoserv info
* memoserv/set-limit - Can set the limit of max stored memos on any user and channel
* memoserv/no-limit - Can send memos through limits and throttles
- * nickserv/access - Can modify other users access and certificate list
+ * nickserv/access - Can modify other users access and certificate lists
* nickserv/alist - Can see the channel access list of other users
* nickserv/auspex - Can see any information with /nickserv info
* nickserv/confirm - Can confirm other users nicknames
diff --git a/modules/commands/cs_entrymsg.cpp b/modules/commands/cs_entrymsg.cpp
index 032d023eb..68be39c53 100644
--- a/modules/commands/cs_entrymsg.cpp
+++ b/modules/commands/cs_entrymsg.cpp
@@ -209,7 +209,7 @@ class CommandEntryMessage : public Command
return;
}
- if (!source.IsFounder(ci) && !source.HasCommand("chanserv/set"))
+ if (!source.IsFounder(ci) && !source.HasPriv("chanserv/administration"))
{
source.Reply(ACCESS_DENIED);
return;
diff --git a/modules/commands/cs_flags.cpp b/modules/commands/cs_flags.cpp
index 05129ad05..0a93e6386 100644
--- a/modules/commands/cs_flags.cpp
+++ b/modules/commands/cs_flags.cpp
@@ -222,7 +222,7 @@ class CommandCSFlags : public Command
}
else
{
- source.Reply(_("Insufficient flags given."));
+ source.Reply(_("\002%s\002 not found on %s access list."), mask.c_str(), ci->name.c_str());
}
return;
}
diff --git a/modules/commands/cs_list.cpp b/modules/commands/cs_list.cpp
index ef9f86dd4..9095f83d7 100644
--- a/modules/commands/cs_list.cpp
+++ b/modules/commands/cs_list.cpp
@@ -170,6 +170,12 @@ class CommandCSSetPrivate : public Command
void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
{
+ if (Anope::ReadOnly)
+ {
+ source.Reply(READ_ONLY_MODE);
+ return;
+ }
+
ChannelInfo *ci = ChannelInfo::Find(params[0]);
if (ci == NULL)
{
diff --git a/modules/commands/ns_access.cpp b/modules/commands/ns_access.cpp
index 5adf17120..ddbcad0a2 100644
--- a/modules/commands/ns_access.cpp
+++ b/modules/commands/ns_access.cpp
@@ -78,8 +78,6 @@ class CommandNSAccess : public Command
{
unsigned i, end;
- Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to view the access list for " << nc->display;
-
if (nc->access.empty())
{
source.Reply(_("%s's access list is empty."), nc->display.c_str());
diff --git a/modules/commands/ns_ajoin.cpp b/modules/commands/ns_ajoin.cpp
index f4f2670f2..2817dd1a6 100644
--- a/modules/commands/ns_ajoin.cpp
+++ b/modules/commands/ns_ajoin.cpp
@@ -92,8 +92,6 @@ class CommandNSAJoin : public Command
{
AJoinList *channels = nc->Require<AJoinList>("ajoinlist");
- Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to view the auto join list for " << nc->display;
-
if ((*channels)->empty())
source.Reply(_("%s's auto join list is empty."), nc->display.c_str());
else
diff --git a/modules/commands/ns_alist.cpp b/modules/commands/ns_alist.cpp
index 467504022..c80a479fc 100644
--- a/modules/commands/ns_alist.cpp
+++ b/modules/commands/ns_alist.cpp
@@ -42,8 +42,6 @@ class CommandNSAList : public Command
nc = na->nc;
}
- Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to view the channel access list for " << nc->display;
-
ListFormatter list(source.GetAccount());
int chan_count = 0;
diff --git a/modules/commands/ns_cert.cpp b/modules/commands/ns_cert.cpp
index ed7140dd5..6b3aa2a14 100644
--- a/modules/commands/ns_cert.cpp
+++ b/modules/commands/ns_cert.cpp
@@ -203,8 +203,6 @@ class CommandNSCert : public Command
{
NSCertList *cl = nc->GetExt<NSCertList>("certificates");
- Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to view the certificate fingerprint list for " << nc->display;
-
if (!cl || !cl->GetCertCount())
{
source.Reply(_("%s's certificate list is empty."), nc->display.c_str());
diff --git a/modules/commands/ns_info.cpp b/modules/commands/ns_info.cpp
index e9a755779..b74e84763 100644
--- a/modules/commands/ns_info.cpp
+++ b/modules/commands/ns_info.cpp
@@ -135,6 +135,12 @@ class CommandNSSetHide : public Command
void Run(CommandSource &source, const Anope::string &user, const Anope::string &param, const Anope::string &arg)
{
+ if (Anope::ReadOnly)
+ {
+ source.Reply(READ_ONLY_MODE);
+ return;
+ }
+
const NickAlias *na = NickAlias::Find(user);
if (!na)
{
diff --git a/modules/commands/ns_list.cpp b/modules/commands/ns_list.cpp
index fbc674eb6..1b6aad5cf 100644
--- a/modules/commands/ns_list.cpp
+++ b/modules/commands/ns_list.cpp
@@ -189,6 +189,12 @@ class CommandNSSetPrivate : public Command
void Run(CommandSource &source, const Anope::string &user, const Anope::string &param)
{
+ if (Anope::ReadOnly)
+ {
+ source.Reply(READ_ONLY_MODE);
+ return;
+ }
+
const NickAlias *na = NickAlias::Find(user);
if (!na)
{
diff --git a/modules/commands/ns_set.cpp b/modules/commands/ns_set.cpp
index 789b0b9c1..deaec6c62 100644
--- a/modules/commands/ns_set.cpp
+++ b/modules/commands/ns_set.cpp
@@ -163,6 +163,12 @@ class CommandNSSASetPassword : public Command
void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
{
+ if (Anope::ReadOnly)
+ {
+ source.Reply(READ_ONLY_MODE);
+ return;
+ }
+
const NickAlias *setter_na = NickAlias::Find(params[0]);
if (setter_na == NULL)
{
@@ -1057,6 +1063,12 @@ class CommandNSSASetNoexpire : public Command
void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
{
+ if (Anope::ReadOnly)
+ {
+ source.Reply(READ_ONLY_MODE);
+ return;
+ }
+
NickAlias *na = NickAlias::Find(params[0]);
if (na == NULL)
{
diff --git a/modules/commands/os_akill.cpp b/modules/commands/os_akill.cpp
index b945058da..b04c48935 100644
--- a/modules/commands/os_akill.cpp
+++ b/modules/commands/os_akill.cpp
@@ -193,7 +193,7 @@ class CommandOSAKill : public Command
source.Reply(_("\002%s\002 added to the AKILL list."), mask.c_str());
- Log(LOG_ADMIN, source, this) << "on " << mask << " (" << x->reason << ") expires in " << (expires ? Anope::Duration(expires - Anope::CurTime) : "never") << " [affects " << affected << " user(s) (" << percent << "%)]";
+ Log(LOG_ADMIN, source, this) << "on " << mask << " (" << x->reason << "), expires in " << (expires ? Anope::Duration(expires - Anope::CurTime) : "never") << " [affects " << affected << " user(s) (" << percent << "%)]";
if (Anope::ReadOnly)
source.Reply(READ_ONLY_MODE);
}
@@ -233,6 +233,7 @@ class CommandOSAKill : public Command
{
FOREACH_MOD(OnDelXLine, (source, x, akills));
+ Log(LOG_ADMIN, source, this) << "to remove " << x->mask << " from the list";
source.Reply(_("\002%s\002 deleted from the AKILL list."), x->mask.c_str());
AkillDelCallback::DoDel(source, x);
}
@@ -358,7 +359,11 @@ class CommandOSAKill : public Command
akills->DelXLine(x);
}
+ Log(LOG_ADMIN, source, this) << "to CLEAR the list";
source.Reply(_("The AKILL list has been cleared."));
+
+ if (Anope::ReadOnly)
+ source.Reply(READ_ONLY_MODE);
}
public:
CommandOSAKill(Module *creator) : Command(creator, "operserv/akill", 1, 2)
diff --git a/modules/commands/os_dns.cpp b/modules/commands/os_dns.cpp
index 9cb51bca2..1a6a6df7f 100644
--- a/modules/commands/os_dns.cpp
+++ b/modules/commands/os_dns.cpp
@@ -289,6 +289,9 @@ class CommandOSDNS : public Command
return;
}
+ if (Anope::ReadOnly)
+ source.Reply(READ_ONLY_MODE);
+
Log(LOG_ADMIN, source, this) << "to add zone " << zone;
new DNSZone(zone);
@@ -306,6 +309,9 @@ class CommandOSDNS : public Command
return;
}
+ if (Anope::ReadOnly)
+ source.Reply(READ_ONLY_MODE);
+
Log(LOG_ADMIN, source, this) << "to delete zone " << z->name;
for (std::set<Anope::string, ci::less>::iterator it = z->servers.begin(), it_end = z->servers.end(); it != it_end; ++it)
@@ -344,6 +350,9 @@ class CommandOSDNS : public Command
return;
}
+ if (Anope::ReadOnly)
+ source.Reply(READ_ONLY_MODE);
+
z->servers.insert(s->GetName());
s->zones.insert(zone);
@@ -365,6 +374,9 @@ class CommandOSDNS : public Command
s = new DNSServer(params[1]);
if (zone.empty())
{
+ if (Anope::ReadOnly)
+ source.Reply(READ_ONLY_MODE);
+
Log(LOG_ADMIN, source, this) << "to add server " << s->GetName();
source.Reply(_("Added server %s."), s->GetName().c_str());
}
@@ -378,6 +390,9 @@ class CommandOSDNS : public Command
return;
}
+ if (Anope::ReadOnly)
+ source.Reply(READ_ONLY_MODE);
+
Log(LOG_ADMIN, source, this) << "to add server " << s->GetName() << " to zone " << zone;
z->servers.insert(s->GetName());
@@ -409,6 +424,9 @@ class CommandOSDNS : public Command
return;
}
+ if (Anope::ReadOnly)
+ source.Reply(READ_ONLY_MODE);
+
Log(LOG_ADMIN, source, this) << "to remove server " << s->GetName() << " from zone " << z->name;
z->servers.erase(s->GetName());
@@ -428,6 +446,9 @@ class CommandOSDNS : public Command
z->servers.erase(s->GetName());
}
+ if (Anope::ReadOnly)
+ source.Reply(READ_ONLY_MODE);
+
Log(LOG_ADMIN, source, this) << "to delete server " << s->GetName();
source.Reply(_("Removed server %s."), s->GetName().c_str());
delete s;
@@ -457,6 +478,9 @@ class CommandOSDNS : public Command
return;
}
+ if (Anope::ReadOnly)
+ source.Reply(READ_ONLY_MODE);
+
s->GetIPs().push_back(params[2]);
source.Reply(_("Added IP %s to %s."), params[2].c_str(), s->GetName().c_str());
Log(LOG_ADMIN, source, this) << "to add IP " << params[2] << " to " << s->GetName();
@@ -479,6 +503,9 @@ class CommandOSDNS : public Command
return;
}
+ if (Anope::ReadOnly)
+ source.Reply(READ_ONLY_MODE);
+
for (unsigned i = 0; i < s->GetIPs().size(); ++i)
if (params[2].equals_ci(s->GetIPs()[i]))
{
@@ -515,6 +542,9 @@ class CommandOSDNS : public Command
return;
}
+ if (Anope::ReadOnly)
+ source.Reply(READ_ONLY_MODE);
+
if (params[2].equals_ci("LIMIT"))
{
try
@@ -560,6 +590,9 @@ class CommandOSDNS : public Command
return;
}
+ if (Anope::ReadOnly)
+ source.Reply(READ_ONLY_MODE);
+
s->SetActive(true);
source.Reply(_("Pooled %s."), s->GetName().c_str());
@@ -582,6 +615,9 @@ class CommandOSDNS : public Command
return;
}
+ if (Anope::ReadOnly)
+ source.Reply(READ_ONLY_MODE);
+
s->Pool(false);
source.Reply(_("Depooled %s."), s->GetName().c_str());
diff --git a/modules/commands/os_forbid.cpp b/modules/commands/os_forbid.cpp
index 4eac5071b..88190ec8d 100644
--- a/modules/commands/os_forbid.cpp
+++ b/modules/commands/os_forbid.cpp
@@ -170,6 +170,9 @@ class CommandOSForbid : public Command
if (created)
this->fs->AddForbid(d);
+ if (Anope::ReadOnly)
+ source.Reply(READ_ONLY_MODE);
+
Log(LOG_ADMIN, source, this) << "to add a forbid on " << entry << " of type " << subcommand;
source.Reply(_("Added a forbid on %s to expire on %s."), entry.c_str(), d->expires ? Anope::strftime(d->expires, source.GetAccount()).c_str() : "never");
@@ -272,6 +275,9 @@ class CommandOSForbid : public Command
ForbidData *d = this->fs->FindForbid(entry, ftype);
if (d != NULL)
{
+ if (Anope::ReadOnly)
+ source.Reply(READ_ONLY_MODE);
+
Log(LOG_ADMIN, source, this) << "to remove forbid on " << d->mask << " of type " << subcommand;
source.Reply(_("%s deleted from the %s forbid list."), d->mask.c_str(), subcommand.c_str());
this->fs->RemoveForbid(d);
diff --git a/modules/commands/os_ignore.cpp b/modules/commands/os_ignore.cpp
index de1351672..a8564a246 100644
--- a/modules/commands/os_ignore.cpp
+++ b/modules/commands/os_ignore.cpp
@@ -180,6 +180,9 @@ class CommandOSIgnore : public Command
return;
}
+ if (Anope::ReadOnly)
+ source.Reply(READ_ONLY_MODE);
+
ignore_service->AddIgnore(mask, source.GetNick(), reason, t);
if (!t)
{
@@ -263,6 +266,9 @@ class CommandOSIgnore : public Command
if (ignore_service->DelIgnore(mask))
{
+ if (Anope::ReadOnly)
+ source.Reply(READ_ONLY_MODE);
+
Log(LOG_ADMIN, source, this) << "to remove an ignore on " << mask;
source.Reply(_("\002%s\002 will no longer be ignored."), mask.c_str());
}
@@ -275,7 +281,11 @@ class CommandOSIgnore : public Command
if (!ignore_service)
return;
+ if (Anope::ReadOnly)
+ source.Reply(READ_ONLY_MODE);
+
ignore_service->ClearIgnores();
+ Log(LOG_ADMIN, source, this) << "to CLEAR the list";
source.Reply(_("Ignore list has been cleared."));
return;
diff --git a/modules/commands/os_info.cpp b/modules/commands/os_info.cpp
index a47999d5e..fb07cd112 100644
--- a/modules/commands/os_info.cpp
+++ b/modules/commands/os_info.cpp
@@ -163,6 +163,9 @@ class CommandOSInfo : public Command
source.Reply(_("Added info to \002%s\002."), target.c_str());
Log(LOG_ADMIN, source, this) << "to add information to " << target;
+
+ if (Anope::ReadOnly)
+ source.Reply(READ_ONLY_MODE);
}
else if (cmd.equals_ci("DEL"))
{
@@ -204,6 +207,9 @@ class CommandOSInfo : public Command
source.Reply(_("Deleted info from \002%s\002."), target.c_str());
Log(LOG_ADMIN, source, this) << "to remove information from " << target;
+
+ if (Anope::ReadOnly)
+ source.Reply(READ_ONLY_MODE);
}
}
else if (cmd.equals_ci("CLEAR"))
@@ -220,6 +226,9 @@ class CommandOSInfo : public Command
source.Reply(_("Cleared info from \002%s\002."), target.c_str());
Log(LOG_ADMIN, source, this) << "to clear information for " << target;
+
+ if (Anope::ReadOnly)
+ source.Reply(READ_ONLY_MODE);
}
else
{
diff --git a/modules/commands/os_news.cpp b/modules/commands/os_news.cpp
index 8fb4cc4f7..5fe8ccc65 100644
--- a/modules/commands/os_news.cpp
+++ b/modules/commands/os_news.cpp
@@ -265,11 +265,11 @@ class CommandOSLogonNews : public NewsBase
this->SendSyntax(source);
source.Reply(" ");
source.Reply(_("Edits or displays the list of logon news messages. When a\n"
- "user connects to the network, these messages will be sent\n"
- "to them. However, no more than \002%d\002 messages will be\n"
- "sent in order to avoid flooding the user. If there are\n"
- "more news messages, only the most recent will be sent."),
- Config->GetModule(this->owner)->Get<unsigned>("newscount", "3"));
+ "user connects to the network, these messages will be sent\n"
+ "to them. However, no more than \002%d\002 messages will be\n"
+ "sent in order to avoid flooding the user. If there are\n"
+ "more news messages, only the most recent will be sent."),
+ Config->GetModule(this->owner)->Get<unsigned>("newscount", "3"));
return true;
}
};
diff --git a/modules/commands/os_oper.cpp b/modules/commands/os_oper.cpp
index bb8c70728..9bc9326fb 100644
--- a/modules/commands/os_oper.cpp
+++ b/modules/commands/os_oper.cpp
@@ -104,6 +104,9 @@ class CommandOSOper : public Command
na->nc->o = new MyOper(na->nc->display, ot);
+ if (Anope::ReadOnly)
+ source.Reply(READ_ONLY_MODE);
+
Log(LOG_ADMIN, source, this) << "ADD " << na->nick << " as type " << ot->GetName();
source.Reply("%s (%s) added to the \002%s\002 list.", na->nick.c_str(), na->nc->display.c_str(), ot->GetName().c_str());
}
@@ -124,6 +127,9 @@ class CommandOSOper : public Command
delete na->nc->o;
na->nc->o = NULL;
+ if (Anope::ReadOnly)
+ source.Reply(READ_ONLY_MODE);
+
Log(LOG_ADMIN, source, this) << "DEL " << na->nick;
source.Reply(_("Oper privileges removed from %s (%s)."), na->nick.c_str(), na->nc->display.c_str());
}
diff --git a/modules/commands/os_set.cpp b/modules/commands/os_set.cpp
index 8b402ee69..06723a230 100644
--- a/modules/commands/os_set.cpp
+++ b/modules/commands/os_set.cpp
@@ -215,10 +215,11 @@ class CommandOSSet : public Command
"users will not be allowed to modify any Services data,\n"
"including channel and nickname access lists, etc. IRCops\n"
"with sufficient Services privileges will be able to modify\n"
- "Services' AKILL list and drop or forbid nicknames and\n"
- "channels, but any such changes will not be saved unless\n"
- "read-only mode is deactivated before Services is terminated\n"
- "or restarted.\n"
+ "Services' AKILL, SQLINE, SNLINE and ignore lists, drop,\n"
+ "suspend or forbid nicknames and channels, and manage news,\n"
+ "oper info and DNS, but any such changes will not be saved\n"
+ "unless read-only mode is deactivated before Services are\n"
+ "terminated or restarted.\n"
" \n"
"This option is equivalent to the command-line option\n"
"\002--readonly\002."));
diff --git a/modules/commands/os_sxline.cpp b/modules/commands/os_sxline.cpp
index 7c460b286..512f48606 100644
--- a/modules/commands/os_sxline.cpp
+++ b/modules/commands/os_sxline.cpp
@@ -95,6 +95,7 @@ class CommandOSSXLineBase : public Command
SXLineDelCallback::DoDel(this->xlm(), source, x);
source.Reply(_("\002%s\002 deleted from the %s list."), mask.c_str(), source.command.c_str());
+ Log(LOG_ADMIN, source, this) << "to remove " << mask << " from the list";
}
if (Anope::ReadOnly)
@@ -207,7 +208,10 @@ class CommandOSSXLineBase : public Command
this->xlm()->DelXLine(x);
}
+ Log(LOG_ADMIN, source, this) << "to CLEAR the list";
source.Reply(_("The %s list has been cleared."), source.command.c_str());
+ if (Anope::ReadOnly)
+ source.Reply(READ_ONLY_MODE);
return;
}
@@ -400,7 +404,7 @@ class CommandOSSNLine : public CommandOSSXLineBase
}
source.Reply(_("\002%s\002 added to the %s list."), mask.c_str(), source.command.c_str());
- Log(LOG_ADMIN, source, this) << "on " << mask << " (" << reason << ") expires in " << (expires ? Anope::Duration(expires - Anope::CurTime) : "never") << " [affects " << affected << " user(s) (" << percent << "%)]";
+ Log(LOG_ADMIN, source, this) << "on " << mask << " (" << reason << "), expires in " << (expires ? Anope::Duration(expires - Anope::CurTime) : "never") << " [affects " << affected << " user(s) (" << percent << "%)]";
if (Anope::ReadOnly)
source.Reply(READ_ONLY_MODE);
}
@@ -428,15 +432,16 @@ class CommandOSSNLine : public CommandOSSXLineBase
"\002SNLINE ADD\002 adds the given realname mask to the SNLINE\n"
"list for the given reason (which \002must\002 be given).\n"
"\037expiry\037 is specified as an integer followed by one of \037d\037\n"
- "(days), \037h\037 (hours), or \037m\037 (minutes). Combinations (such as\n"
- "\0371h30m\037) are not permitted. If a unit specifier is not\n"
+ "(days), \037h\037 (hours), or \037m\037 (minutes). Combinations (such as\n"
+ "\0371h30m\037) are not permitted. If a unit specifier is not\n"
"included, the default is days (so \037+30\037 by itself means 30\n"
- "days). To add an SNLINE which does not expire, use \037+0\037. If the\n"
+ "days). To add an SNLINE which does not expire, use \037+0\037. If the\n"
"realname mask to be added starts with a \037+\037, an expiry time must\n"
"be given, even if it is the same as the default. The\n"
"current SNLINE default expiry time can be found with the\n"
"\002STATS AKILL\002 command.\n"
- "Note: because the realname mask may contain spaces, the\n"
+ " \n"
+ "\002Note\002: because the realname mask may contain spaces, the\n"
"separator between it and the reason is a colon."));
const Anope::string &regexengine = Config->GetBlock("options")->Get<const Anope::string>("regexengine");
if (!regexengine.empty())
@@ -631,9 +636,8 @@ class CommandOSSQLine : public CommandOSSXLineBase
this->xlm()->Send(NULL, x);
}
- source.Reply(_("\002%s\002 added to the SQLINE list."), mask.c_str());
- Log(LOG_ADMIN, source, this) << "on " << mask << " (" << reason << ") expires in " << (expires ? Anope::Duration(expires - Anope::CurTime) : "never") << " [affects " << affected << " user(s) (" << percent << "%)]";
-
+ source.Reply(_("\002%s\002 added to the %s list."), mask.c_str(), source.command.c_str());
+ Log(LOG_ADMIN, source, this) << "on " << mask << " (" << reason << "), expires in " << (expires ? Anope::Duration(expires - Anope::CurTime) : "never") << " [affects " << affected << " user(s) (" << percent << "%)]";
if (Anope::ReadOnly)
source.Reply(READ_ONLY_MODE);
}