summaryrefslogtreecommitdiff
path: root/src/commands
diff options
context:
space:
mode:
Diffstat (limited to 'src/commands')
-rw-r--r--src/commands/cmd_away.cpp4
-rw-r--r--src/commands/cmd_clearcache.cpp52
-rw-r--r--src/commands/cmd_commands.cpp9
-rw-r--r--src/commands/cmd_die.cpp4
-rw-r--r--src/commands/cmd_dns.cpp842
-rw-r--r--src/commands/cmd_eline.cpp17
-rw-r--r--src/commands/cmd_gline.cpp10
-rw-r--r--src/commands/cmd_hostname_lookup.cpp239
-rw-r--r--src/commands/cmd_info.cpp2
-rw-r--r--src/commands/cmd_invite.cpp19
-rw-r--r--src/commands/cmd_join.cpp26
-rw-r--r--src/commands/cmd_kick.cpp25
-rw-r--r--src/commands/cmd_kill.cpp34
-rw-r--r--src/commands/cmd_kline.cpp10
-rw-r--r--src/commands/cmd_list.cpp16
-rw-r--r--src/commands/cmd_lusers.cpp36
-rw-r--r--src/commands/cmd_map.cpp58
-rw-r--r--src/commands/cmd_mode.cpp6
-rw-r--r--src/commands/cmd_motd.cpp5
-rw-r--r--src/commands/cmd_names.cpp14
-rw-r--r--src/commands/cmd_nick.cpp2
-rw-r--r--src/commands/cmd_notice.cpp229
-rw-r--r--src/commands/cmd_oper.cpp26
-rw-r--r--src/commands/cmd_part.cpp6
-rw-r--r--src/commands/cmd_privmsg.cpp115
-rw-r--r--src/commands/cmd_qline.cpp8
-rw-r--r--src/commands/cmd_quit.cpp4
-rw-r--r--src/commands/cmd_rehash.cpp9
-rw-r--r--src/commands/cmd_restart.cpp2
-rw-r--r--src/commands/cmd_rules.cpp5
-rw-r--r--src/commands/cmd_stats.cpp53
-rw-r--r--src/commands/cmd_time.cpp13
-rw-r--r--src/commands/cmd_topic.cpp47
-rw-r--r--src/commands/cmd_unloadmodule.cpp9
-rw-r--r--src/commands/cmd_user.cpp4
-rw-r--r--src/commands/cmd_userhost.cpp4
-rw-r--r--src/commands/cmd_version.cpp8
-rw-r--r--src/commands/cmd_wallops.cpp19
-rw-r--r--src/commands/cmd_who.cpp84
-rw-r--r--src/commands/cmd_whois.cpp185
-rw-r--r--src/commands/cmd_whowas.cpp272
-rw-r--r--src/commands/cmd_zline.cpp9
42 files changed, 1755 insertions, 786 deletions
diff --git a/src/commands/cmd_away.cpp b/src/commands/cmd_away.cpp
index fa3f7fae9..de0969af4 100644
--- a/src/commands/cmd_away.cpp
+++ b/src/commands/cmd_away.cpp
@@ -38,6 +38,10 @@ class CommandAway : public Command
* @return A value from CmdResult to indicate command success or failure.
*/
CmdResult Handle(const std::vector<std::string>& parameters, User *user);
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ {
+ return (IS_LOCAL(user) ? ROUTE_LOCALONLY : ROUTE_BROADCAST);
+ }
};
/** Handle /AWAY
diff --git a/src/commands/cmd_clearcache.cpp b/src/commands/cmd_clearcache.cpp
deleted file mode 100644
index 5914f9a8f..000000000
--- a/src/commands/cmd_clearcache.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-/** Handle /CLEARCACHE. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandClearcache : public Command
-{
- public:
- /** Constructor for clearcache.
- */
- CommandClearcache ( Module* parent) : Command(parent,"CLEARCACHE",0) { flags_needed = 'o'; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-/** Handle /CLEARCACHE
- */
-CmdResult CommandClearcache::Handle (const std::vector<std::string>& parameters, User *user)
-{
- int n = ServerInstance->Res->ClearCache();
- user->WriteServ("NOTICE %s :*** Cleared DNS cache of %d items.", user->nick.c_str(), n);
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandClearcache)
diff --git a/src/commands/cmd_commands.cpp b/src/commands/cmd_commands.cpp
index 36408b363..99acdfe96 100644
--- a/src/commands/cmd_commands.cpp
+++ b/src/commands/cmd_commands.cpp
@@ -53,12 +53,9 @@ CmdResult CommandCommands::Handle (const std::vector<std::string>&, User *user)
continue;
Module* src = i->second->creator;
- char buffer[MAXBUF];
- snprintf(buffer, MAXBUF, ":%s %03d %s :%s %s %d %d",
- ServerInstance->Config->ServerName.c_str(), RPL_COMMANDS, user->nick.c_str(),
- i->second->name.c_str(), src->ModuleSourceFile.c_str(),
- i->second->min_params, i->second->Penalty);
- list.push_back(buffer);
+ list.push_back(InspIRCd::Format(":%s %03d %s :%s %s %d %d", ServerInstance->Config->ServerName.c_str(),
+ RPL_COMMANDS, user->nick.c_str(), i->second->name.c_str(), src->ModuleSourceFile.c_str(),
+ i->second->min_params, i->second->Penalty));
}
sort(list.begin(), list.end());
for(unsigned int i=0; i < list.size(); i++)
diff --git a/src/commands/cmd_die.cpp b/src/commands/cmd_die.cpp
index 1d6640213..3ccc037b4 100644
--- a/src/commands/cmd_die.cpp
+++ b/src/commands/cmd_die.cpp
@@ -50,7 +50,7 @@ CmdResult CommandDie::Handle (const std::vector<std::string>& parameters, User *
{
{
std::string diebuf = "*** DIE command from " + user->GetFullHost() + ". Terminating.";
- ServerInstance->Logs->Log("COMMAND",SPARSE, diebuf);
+ ServerInstance->Logs->Log("COMMAND", LOG_SPARSE, diebuf);
ServerInstance->SendError(diebuf);
}
@@ -58,7 +58,7 @@ CmdResult CommandDie::Handle (const std::vector<std::string>& parameters, User *
}
else
{
- ServerInstance->Logs->Log("COMMAND",SPARSE, "Failed /DIE command from %s", user->GetFullRealHost().c_str());
+ ServerInstance->Logs->Log("COMMAND", LOG_SPARSE, "Failed /DIE command from %s", user->GetFullRealHost().c_str());
ServerInstance->SNO->WriteGlobalSno('a', "Failed DIE Command from %s.", user->GetFullRealHost().c_str());
return CMD_FAILURE;
}
diff --git a/src/commands/cmd_dns.cpp b/src/commands/cmd_dns.cpp
new file mode 100644
index 000000000..2b7114128
--- /dev/null
+++ b/src/commands/cmd_dns.cpp
@@ -0,0 +1,842 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Adam <Adam@anope.org>
+ * Copyright (C) 2003-2013 Anope Team <team@anope.org>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "inspircd.h"
+#include "modules/dns.h"
+#include <iostream>
+#include <fstream>
+
+#ifdef _WIN32
+#include <Iphlpapi.h>
+#pragma comment(lib, "Iphlpapi.lib")
+#endif
+
+using namespace DNS;
+
+/** A full packet sent or recieved to/from the nameserver
+ */
+class Packet : public Query
+{
+ void PackName(unsigned char* output, unsigned short output_size, unsigned short& pos, const std::string& name)
+ {
+ if (pos + name.length() + 2 > output_size)
+ throw Exception("Unable to pack name");
+
+ ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: Packing name " + name);
+
+ irc::sepstream sep(name, '.');
+ std::string token;
+
+ while (sep.GetToken(token))
+ {
+ output[pos++] = token.length();
+ memcpy(&output[pos], token.data(), token.length());
+ pos += token.length();
+ }
+
+ output[pos++] = 0;
+ }
+
+ std::string UnpackName(const unsigned char* input, unsigned short input_size, unsigned short& pos)
+ {
+ std::string name;
+ unsigned short pos_ptr = pos, lowest_ptr = input_size;
+ bool compressed = false;
+
+ if (pos_ptr >= input_size)
+ throw Exception("Unable to unpack name - no input");
+
+ while (input[pos_ptr] > 0)
+ {
+ unsigned short offset = input[pos_ptr];
+
+ if (offset & POINTER)
+ {
+ if ((offset & POINTER) != POINTER)
+ throw Exception("Unable to unpack name - bogus compression header");
+ if (pos_ptr + 1 >= input_size)
+ throw Exception("Unable to unpack name - bogus compression header");
+
+ /* Place pos at the second byte of the first (farthest) compression pointer */
+ if (compressed == false)
+ {
+ ++pos;
+ compressed = true;
+ }
+
+ pos_ptr = (offset & LABEL) << 8 | input[pos_ptr + 1];
+
+ /* Pointers can only go back */
+ if (pos_ptr >= lowest_ptr)
+ throw Exception("Unable to unpack name - bogus compression pointer");
+ lowest_ptr = pos_ptr;
+ }
+ else
+ {
+ if (pos_ptr + offset + 1 >= input_size)
+ throw Exception("Unable to unpack name - offset too large");
+ if (!name.empty())
+ name += ".";
+ for (unsigned i = 1; i <= offset; ++i)
+ name += input[pos_ptr + i];
+
+ pos_ptr += offset + 1;
+ if (compressed == false)
+ /* Move up pos */
+ pos = pos_ptr;
+ }
+ }
+
+ /* +1 pos either to one byte after the compression pointer or one byte after the ending \0 */
+ ++pos;
+
+ if (name.empty())
+ throw Exception("Unable to unpack name - no name");
+
+ ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: Unpack name " + name);
+
+ return name;
+ }
+
+ Question UnpackQuestion(const unsigned char* input, unsigned short input_size, unsigned short& pos)
+ {
+ Question question;
+
+ question.name = this->UnpackName(input, input_size, pos);
+
+ if (pos + 4 > input_size)
+ throw Exception("Unable to unpack question");
+
+ question.type = static_cast<QueryType>(input[pos] << 8 | input[pos + 1]);
+ pos += 2;
+
+ question.qclass = input[pos] << 8 | input[pos + 1];
+ pos += 2;
+
+ return question;
+ }
+
+ ResourceRecord UnpackResourceRecord(const unsigned char* input, unsigned short input_size, unsigned short& pos)
+ {
+ ResourceRecord record = static_cast<ResourceRecord>(this->UnpackQuestion(input, input_size, pos));
+
+ if (pos + 6 > input_size)
+ throw Exception("Unable to unpack resource record");
+
+ record.ttl = (input[pos] << 24) | (input[pos + 1] << 16) | (input[pos + 2] << 8) | input[pos + 3];
+ pos += 4;
+
+ //record.rdlength = input[pos] << 8 | input[pos + 1];
+ pos += 2;
+
+ switch (record.type)
+ {
+ case QUERY_A:
+ {
+ if (pos + 4 > input_size)
+ throw Exception("Unable to unpack resource record");
+
+ irc::sockets::sockaddrs addrs;
+ memset(&addrs, 0, sizeof(addrs));
+
+ addrs.in4.sin_family = AF_INET;
+ addrs.in4.sin_addr.s_addr = input[pos] | (input[pos + 1] << 8) | (input[pos + 2] << 16) | (input[pos + 3] << 24);
+ pos += 4;
+
+ record.rdata = addrs.addr();
+ break;
+ }
+ case QUERY_AAAA:
+ {
+ if (pos + 16 > input_size)
+ throw Exception("Unable to unpack resource record");
+
+ irc::sockets::sockaddrs addrs;
+ memset(&addrs, 0, sizeof(addrs));
+
+ addrs.in6.sin6_family = AF_INET6;
+ for (int j = 0; j < 16; ++j)
+ addrs.in6.sin6_addr.s6_addr[j] = input[pos + j];
+ pos += 16;
+
+ record.rdata = addrs.addr();
+
+ break;
+ }
+ case QUERY_CNAME:
+ case QUERY_PTR:
+ {
+ record.rdata = this->UnpackName(input, input_size, pos);
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (!record.name.empty() && !record.rdata.empty())
+ ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: " + record.name + " -> " + record.rdata);
+
+ return record;
+ }
+
+ public:
+ static const int POINTER = 0xC0;
+ static const int LABEL = 0x3F;
+ static const int HEADER_LENGTH = 12;
+
+ /* ID for this packet */
+ unsigned short id;
+ /* Flags on the packet */
+ unsigned short flags;
+
+ Packet() : id(0), flags(0)
+ {
+ }
+
+ void Fill(const unsigned char* input, const unsigned short len)
+ {
+ if (len < HEADER_LENGTH)
+ throw Exception("Unable to fill packet");
+
+ unsigned short packet_pos = 0;
+
+ this->id = (input[packet_pos] << 8) | input[packet_pos + 1];
+ packet_pos += 2;
+
+ if (this->id >= MAX_REQUEST_ID)
+ throw Exception("Query ID too large?");
+
+ this->flags = (input[packet_pos] << 8) | input[packet_pos + 1];
+ packet_pos += 2;
+
+ unsigned short qdcount = (input[packet_pos] << 8) | input[packet_pos + 1];
+ packet_pos += 2;
+
+ unsigned short ancount = (input[packet_pos] << 8) | input[packet_pos + 1];
+ packet_pos += 2;
+
+ unsigned short nscount = (input[packet_pos] << 8) | input[packet_pos + 1];
+ packet_pos += 2;
+
+ unsigned short arcount = (input[packet_pos] << 8) | input[packet_pos + 1];
+ packet_pos += 2;
+
+ ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: qdcount: " + ConvToStr(qdcount) + " ancount: " + ConvToStr(ancount) + " nscount: " + ConvToStr(nscount) + " arcount: " + ConvToStr(arcount));
+
+ for (unsigned i = 0; i < qdcount; ++i)
+ this->questions.push_back(this->UnpackQuestion(input, len, packet_pos));
+
+ for (unsigned i = 0; i < ancount; ++i)
+ this->answers.push_back(this->UnpackResourceRecord(input, len, packet_pos));
+ }
+
+ unsigned short Pack(unsigned char* output, unsigned short output_size)
+ {
+ if (output_size < HEADER_LENGTH)
+ throw Exception("Unable to pack packet");
+
+ unsigned short pos = 0;
+
+ output[pos++] = this->id >> 8;
+ output[pos++] = this->id & 0xFF;
+ output[pos++] = this->flags >> 8;
+ output[pos++] = this->flags & 0xFF;
+ output[pos++] = this->questions.size() >> 8;
+ output[pos++] = this->questions.size() & 0xFF;
+ output[pos++] = this->answers.size() >> 8;
+ output[pos++] = this->answers.size() & 0xFF;
+ output[pos++] = 0;
+ output[pos++] = 0;
+ output[pos++] = 0;
+ output[pos++] = 0;
+
+ for (unsigned i = 0; i < this->questions.size(); ++i)
+ {
+ Question& q = this->questions[i];
+
+ if (q.type == QUERY_PTR)
+ {
+ irc::sockets::sockaddrs ip;
+ irc::sockets::aptosa(q.name, 0, ip);
+
+ if (q.name.find(':') != std::string::npos)
+ {
+ static const char* const hex = "0123456789abcdef";
+ char reverse_ip[128];
+ unsigned reverse_ip_count = 0;
+ for (int j = 15; j >= 0; --j)
+ {
+ reverse_ip[reverse_ip_count++] = hex[ip.in6.sin6_addr.s6_addr[j] & 0xF];
+ reverse_ip[reverse_ip_count++] = '.';
+ reverse_ip[reverse_ip_count++] = hex[ip.in6.sin6_addr.s6_addr[j] >> 4];
+ reverse_ip[reverse_ip_count++] = '.';
+ }
+ reverse_ip[reverse_ip_count++] = 0;
+
+ q.name = reverse_ip;
+ q.name += "ip6.arpa";
+ }
+ else
+ {
+ unsigned long forward = ip.in4.sin_addr.s_addr;
+ ip.in4.sin_addr.s_addr = forward << 24 | (forward & 0xFF00) << 8 | (forward & 0xFF0000) >> 8 | forward >> 24;
+
+ q.name = ip.addr() + ".in-addr.arpa";
+ }
+ }
+
+ this->PackName(output, output_size, pos, q.name);
+
+ if (pos + 4 >= output_size)
+ throw Exception("Unable to pack packet");
+
+ short s = htons(q.type);
+ memcpy(&output[pos], &s, 2);
+ pos += 2;
+
+ s = htons(q.qclass);
+ memcpy(&output[pos], &s, 2);
+ pos += 2;
+ }
+
+ for (unsigned int i = 0; i < answers.size(); i++)
+ {
+ ResourceRecord& rr = answers[i];
+
+ this->PackName(output, output_size, pos, rr.name);
+
+ if (pos + 8 >= output_size)
+ throw Exception("Unable to pack packet");
+
+ short s = htons(rr.type);
+ memcpy(&output[pos], &s, 2);
+ pos += 2;
+
+ s = htons(rr.qclass);
+ memcpy(&output[pos], &s, 2);
+ pos += 2;
+
+ long l = htonl(rr.ttl);
+ memcpy(&output[pos], &l, 4);
+ pos += 4;
+
+ switch (rr.type)
+ {
+ case QUERY_A:
+ {
+ if (pos + 6 > output_size)
+ throw Exception("Unable to pack packet");
+
+ irc::sockets::sockaddrs a;
+ irc::sockets::aptosa(rr.rdata, 0, a);
+
+ s = htons(4);
+ memcpy(&output[pos], &s, 2);
+ pos += 2;
+
+ memcpy(&output[pos], &a.in4.sin_addr, 4);
+ pos += 4;
+ break;
+ }
+ case QUERY_AAAA:
+ {
+ if (pos + 18 > output_size)
+ throw Exception("Unable to pack packet");
+
+ irc::sockets::sockaddrs a;
+ irc::sockets::aptosa(rr.rdata, 0, a);
+
+ s = htons(16);
+ memcpy(&output[pos], &s, 2);
+ pos += 2;
+
+ memcpy(&output[pos], &a.in6.sin6_addr, 16);
+ pos += 16;
+ break;
+ }
+ case QUERY_CNAME:
+ case QUERY_PTR:
+ {
+ if (pos + 2 >= output_size)
+ throw Exception("Unable to pack packet");
+
+ unsigned short packet_pos_save = pos;
+ pos += 2;
+
+ this->PackName(output, output_size, pos, rr.rdata);
+
+ s = htons(pos - packet_pos_save - 2);
+ memcpy(&output[packet_pos_save], &s, 2);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ return pos;
+ }
+};
+
+class MyManager : public Manager, public Timer, public EventHandler
+{
+ typedef TR1NS::unordered_map<Question, Query, Question::hash> cache_map;
+ cache_map cache;
+
+ irc::sockets::sockaddrs myserver;
+
+ static bool IsExpired(const Query& record, time_t now = ServerInstance->Time())
+ {
+ const ResourceRecord& req = record.answers[0];
+ return (req.created + static_cast<time_t>(req.ttl) < now);
+ }
+
+ /** Check the DNS cache to see if request can be handled by a cached result
+ * @return true if a cached result was found.
+ */
+ bool CheckCache(DNS::Request* req, const DNS::Question& question)
+ {
+ ServerInstance->Logs->Log("RESOLVER", LOG_SPARSE, "Resolver: cache: Checking cache for " + question.name);
+
+ cache_map::iterator it = this->cache.find(question);
+ if (it == this->cache.end())
+ return false;
+
+ Query& record = it->second;
+ if (IsExpired(record))
+ {
+ this->cache.erase(it);
+ return false;
+ }
+
+ ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: cache: Using cached result for " + question.name);
+ record.cached = true;
+ req->OnLookupComplete(&record);
+ return true;
+ }
+
+ /** Add a record to the dns cache
+ * @param r The record
+ */
+ void AddCache(Query& r)
+ {
+ const ResourceRecord& rr = r.answers[0];
+ ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: cache: added cache for " + rr.name + " -> " + rr.rdata + " ttl: " + ConvToStr(rr.ttl));
+ this->cache[r.questions[0]] = r;
+ }
+
+ public:
+ DNS::Request* requests[MAX_REQUEST_ID];
+
+ MyManager(Module* c) : Manager(c), Timer(3600, ServerInstance->Time(), true)
+ {
+ for (int i = 0; i < MAX_REQUEST_ID; ++i)
+ requests[i] = NULL;
+ ServerInstance->Timers->AddTimer(this);
+ }
+
+ ~MyManager()
+ {
+ for (int i = 0; i < MAX_REQUEST_ID; ++i)
+ {
+ DNS::Request* request = requests[i];
+ if (!request)
+ continue;
+
+ Query rr(*request);
+ rr.error = ERROR_UNKNOWN;
+ request->OnError(&rr);
+
+ delete request;
+ }
+ }
+
+ void Process(DNS::Request* req)
+ {
+ ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: Processing request to lookup " + req->name + " of type " + ConvToStr(req->type) + " to " + this->myserver.addr());
+
+ /* Create an id */
+ unsigned int tries = 0;
+ do
+ {
+ req->id = ServerInstance->GenRandomInt(DNS::MAX_REQUEST_ID);
+
+ if (++tries == DNS::MAX_REQUEST_ID*5)
+ {
+ // If we couldn't find an empty slot this many times, do a sequential scan as a last
+ // resort. If an empty slot is found that way, go on, otherwise throw an exception
+ req->id = 0;
+ for (int i = 1; i < DNS::MAX_REQUEST_ID; i++)
+ {
+ if (!this->requests[i])
+ {
+ req->id = i;
+ break;
+ }
+ }
+
+ if (req->id == 0)
+ throw Exception("DNS: All ids are in use");
+
+ break;
+ }
+ }
+ while (!req->id || this->requests[req->id]);
+
+ this->requests[req->id] = req;
+
+ Packet p;
+ p.flags = QUERYFLAGS_RD;
+ p.id = req->id;
+ p.questions.push_back(*req);
+
+ unsigned char buffer[524];
+ unsigned short len = p.Pack(buffer, sizeof(buffer));
+
+ /* Note that calling Pack() above can actually change the contents of p.questions[0].name, if the query is a PTR,
+ * to contain the value that would be in the DNS cache, which is why this is here.
+ */
+ if (req->use_cache && this->CheckCache(req, p.questions[0]))
+ {
+ ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: Using cached result");
+ delete req;
+ return;
+ }
+
+ if (ServerInstance->SE->SendTo(this, buffer, len, 0, &this->myserver.sa, this->myserver.sa_size()) != len)
+ throw Exception("DNS: Unable to send query");
+ }
+
+ void RemoveRequest(DNS::Request* req)
+ {
+ this->requests[req->id] = NULL;
+ }
+
+ std::string GetErrorStr(Error e)
+ {
+ switch (e)
+ {
+ case ERROR_UNLOADED:
+ return "Module is unloading";
+ case ERROR_TIMEDOUT:
+ return "Request timed out";
+ case ERROR_NOT_AN_ANSWER:
+ case ERROR_NONSTANDARD_QUERY:
+ case ERROR_FORMAT_ERROR:
+ return "Malformed answer";
+ case ERROR_SERVER_FAILURE:
+ case ERROR_NOT_IMPLEMENTED:
+ case ERROR_REFUSED:
+ case ERROR_INVALIDTYPE:
+ return "Nameserver failure";
+ case ERROR_DOMAIN_NOT_FOUND:
+ case ERROR_NO_RECORDS:
+ return "Domain not found";
+ case ERROR_NONE:
+ case ERROR_UNKNOWN:
+ default:
+ return "Unknown error";
+ }
+ }
+
+ void HandleEvent(EventType et, int)
+ {
+ if (et == EVENT_ERROR)
+ {
+ ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: UDP socket got an error event");
+ return;
+ }
+
+ unsigned char buffer[524];
+ irc::sockets::sockaddrs from;
+ socklen_t x = sizeof(from);
+
+ int length = ServerInstance->SE->RecvFrom(this, buffer, sizeof(buffer), 0, &from.sa, &x);
+
+ if (length < Packet::HEADER_LENGTH)
+ return;
+
+ Packet recv_packet;
+
+ try
+ {
+ recv_packet.Fill(buffer, length);
+ }
+ catch (Exception& ex)
+ {
+ ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, std::string(ex.GetReason()));
+ return;
+ }
+
+ if (myserver != from)
+ {
+ std::string server1 = from.str();
+ std::string server2 = myserver.str();
+ ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: Got a result from the wrong server! Bad NAT or DNS forging attempt? '%s' != '%s'",
+ server1.c_str(), server2.c_str());
+ return;
+ }
+
+ DNS::Request* request = this->requests[recv_packet.id];
+ if (request == NULL)
+ {
+ ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: Received an answer for something we didn't request");
+ return;
+ }
+
+ if (recv_packet.flags & QUERYFLAGS_OPCODE)
+ {
+ ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: Received a nonstandard query");
+ ServerInstance->stats->statsDnsBad++;
+ recv_packet.error = ERROR_NONSTANDARD_QUERY;
+ request->OnError(&recv_packet);
+ }
+ else if (recv_packet.flags & QUERYFLAGS_RCODE)
+ {
+ Error error = ERROR_UNKNOWN;
+
+ switch (recv_packet.flags & QUERYFLAGS_RCODE)
+ {
+ case 1:
+ ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: format error");
+ error = ERROR_FORMAT_ERROR;
+ break;
+ case 2:
+ ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: server error");
+ error = ERROR_SERVER_FAILURE;
+ break;
+ case 3:
+ ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: domain not found");
+ error = ERROR_DOMAIN_NOT_FOUND;
+ break;
+ case 4:
+ ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: not implemented");
+ error = ERROR_NOT_IMPLEMENTED;
+ break;
+ case 5:
+ ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: refused");
+ error = ERROR_REFUSED;
+ break;
+ default:
+ break;
+ }
+
+ ServerInstance->stats->statsDnsBad++;
+ recv_packet.error = error;
+ request->OnError(&recv_packet);
+ }
+ else if (recv_packet.questions.empty() || recv_packet.answers.empty())
+ {
+ ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: No resource records returned");
+ ServerInstance->stats->statsDnsBad++;
+ recv_packet.error = ERROR_NO_RECORDS;
+ request->OnError(&recv_packet);
+ }
+ else
+ {
+ ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: Lookup complete for " + request->name);
+ ServerInstance->stats->statsDnsGood++;
+ request->OnLookupComplete(&recv_packet);
+ this->AddCache(recv_packet);
+ }
+
+ ServerInstance->stats->statsDns++;
+
+ /* Request's destructor removes it from the request map */
+ delete request;
+ }
+
+ bool Tick(time_t now)
+ {
+ ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolver: cache: purging DNS cache");
+
+ for (cache_map::iterator it = this->cache.begin(); it != this->cache.end(); )
+ {
+ const Query& query = it->second;
+ if (IsExpired(query, now))
+ this->cache.erase(it++);
+ else
+ ++it;
+ }
+ return true;
+ }
+
+ void Rehash(const std::string& dnsserver)
+ {
+ if (this->GetFd() > -1)
+ {
+ ServerInstance->SE->DelFd(this);
+ ServerInstance->SE->Shutdown(this, 2);
+ ServerInstance->SE->Close(this);
+ this->SetFd(-1);
+
+ /* Remove expired entries from the cache */
+ this->Tick(ServerInstance->Time());
+ }
+
+ irc::sockets::aptosa(dnsserver, DNS::PORT, myserver);
+
+ /* Initialize mastersocket */
+ int s = socket(myserver.sa.sa_family, SOCK_DGRAM, 0);
+ this->SetFd(s);
+
+ /* Have we got a socket? */
+ if (this->GetFd() != -1)
+ {
+ ServerInstance->SE->SetReuse(s);
+ ServerInstance->SE->NonBlocking(s);
+
+ irc::sockets::sockaddrs bindto;
+ memset(&bindto, 0, sizeof(bindto));
+ bindto.sa.sa_family = myserver.sa.sa_family;
+
+ if (ServerInstance->SE->Bind(this->GetFd(), bindto) < 0)
+ {
+ /* Failed to bind */
+ ServerInstance->Logs->Log("RESOLVER", LOG_SPARSE, "Resolver: Error binding dns socket - hostnames will NOT resolve");
+ ServerInstance->SE->Close(this);
+ this->SetFd(-1);
+ }
+ else if (!ServerInstance->SE->AddFd(this, FD_WANT_POLL_READ | FD_WANT_NO_WRITE))
+ {
+ ServerInstance->Logs->Log("RESOLVER", LOG_SPARSE, "Resolver: Internal error starting DNS - hostnames will NOT resolve.");
+ ServerInstance->SE->Close(this);
+ this->SetFd(-1);
+ }
+ }
+ else
+ {
+ ServerInstance->Logs->Log("RESOLVER", LOG_SPARSE, "Resolver: Error creating DNS socket - hostnames will NOT resolve");
+ }
+ }
+};
+
+class ModuleDNS : public Module
+{
+ MyManager manager;
+ std::string DNSServer;
+
+ void FindDNSServer()
+ {
+#ifdef _WIN32
+ // attempt to look up their nameserver from the system
+ ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "WARNING: <dns:server> not defined, attempting to find a working server in the system settings...");
+
+ PFIXED_INFO pFixedInfo;
+ DWORD dwBufferSize = sizeof(FIXED_INFO);
+ pFixedInfo = (PFIXED_INFO) HeapAlloc(GetProcessHeap(), 0, sizeof(FIXED_INFO));
+
+ if (pFixedInfo)
+ {
+ if (GetNetworkParams(pFixedInfo, &dwBufferSize) == ERROR_BUFFER_OVERFLOW)
+ {
+ HeapFree(GetProcessHeap(), 0, pFixedInfo);
+ pFixedInfo = (PFIXED_INFO) HeapAlloc(GetProcessHeap(), 0, dwBufferSize);
+ }
+
+ if (pFixedInfo)
+ {
+ if (GetNetworkParams(pFixedInfo, &dwBufferSize) == NO_ERROR)
+ DNSServer = pFixedInfo->DnsServerList.IpAddress.String;
+
+ HeapFree(GetProcessHeap(), 0, pFixedInfo);
+ }
+
+ if (!DNSServer.empty())
+ {
+ ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "<dns:server> set to '%s' as first active resolver in the system settings.", DNSServer.c_str());
+ return;
+ }
+ }
+
+ ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "No viable nameserver found! Defaulting to nameserver '127.0.0.1'!");
+#else
+ // attempt to look up their nameserver from /etc/resolv.conf
+ ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "WARNING: <dns:server> not defined, attempting to find working server in /etc/resolv.conf...");
+
+ std::ifstream resolv("/etc/resolv.conf");
+
+ while (resolv >> DNSServer)
+ {
+ if (DNSServer == "nameserver")
+ {
+ resolv >> DNSServer;
+ if (DNSServer.find_first_not_of("0123456789.") == std::string::npos)
+ {
+ ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "<dns:server> set to '%s' as first resolver in /etc/resolv.conf.",DNSServer.c_str());
+ return;
+ }
+ }
+ }
+
+ ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "/etc/resolv.conf contains no viable nameserver entries! Defaulting to nameserver '127.0.0.1'!");
+#endif
+ DNSServer = "127.0.0.1";
+ }
+
+ public:
+ ModuleDNS() : manager(this)
+ {
+ }
+
+ void init()
+ {
+ ServerInstance->Modules->AddService(this->manager);
+
+ this->OnRehash(NULL);
+ }
+
+ void OnRehash(User* user)
+ {
+ std::string oldserver = DNSServer;
+ DNSServer = ServerInstance->Config->ConfValue("dns")->getString("server");
+ if (DNSServer.empty())
+ FindDNSServer();
+
+ if (oldserver != DNSServer)
+ this->manager.Rehash(DNSServer);
+ }
+
+ void OnUnloadModule(Module* mod)
+ {
+ for (int i = 0; i < MAX_REQUEST_ID; ++i)
+ {
+ DNS::Request* req = this->manager.requests[i];
+ if (!req)
+ continue;
+
+ if (req->creator == mod)
+ {
+ Query rr(*req);
+ rr.error = ERROR_UNLOADED;
+ req->OnError(&rr);
+
+ delete req;
+ }
+ }
+ }
+
+ Version GetVersion()
+ {
+ return Version("DNS support", VF_CORE|VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleDNS)
+
diff --git a/src/commands/cmd_eline.cpp b/src/commands/cmd_eline.cpp
index ca39f9061..67f67e9f0 100644
--- a/src/commands/cmd_eline.cpp
+++ b/src/commands/cmd_eline.cpp
@@ -60,17 +60,16 @@ CmdResult CommandEline::Handle (const std::vector<std::string>& parameters, User
else
ih = ServerInstance->XLines->IdentSplit(target);
- if (ih.first.empty())
- {
- user->WriteServ("NOTICE %s :*** Target not found", user->nick.c_str());
- return CMD_FAILURE;
- }
+ if (ih.first.empty())
+ {
+ user->WriteNotice("*** Target not found");
+ return CMD_FAILURE;
+ }
if (ServerInstance->HostMatchesEveryone(ih.first+"@"+ih.second,user))
return CMD_FAILURE;
- long duration = ServerInstance->Duration(parameters[1].c_str());
-
+ unsigned long duration = InspIRCd::Duration(parameters[1]);
ELine* el = new ELine(ServerInstance->Time(), duration, user->nick.c_str(), parameters[2].c_str(), ih.first.c_str(), ih.second.c_str());
if (ServerInstance->XLines->AddLine(el, user))
{
@@ -89,7 +88,7 @@ CmdResult CommandEline::Handle (const std::vector<std::string>& parameters, User
else
{
delete el;
- user->WriteServ("NOTICE %s :*** E-Line for %s already exists",user->nick.c_str(),target.c_str());
+ user->WriteNotice("*** E-Line for " + target + " already exists");
}
}
else
@@ -100,7 +99,7 @@ CmdResult CommandEline::Handle (const std::vector<std::string>& parameters, User
}
else
{
- user->WriteServ("NOTICE %s :*** E-Line %s not found in list, try /stats e.",user->nick.c_str(),target.c_str());
+ user->WriteNotice("*** E-Line " + target + " not found in list, try /stats e");
}
}
diff --git a/src/commands/cmd_gline.cpp b/src/commands/cmd_gline.cpp
index 6505b7464..bdb5c26b2 100644
--- a/src/commands/cmd_gline.cpp
+++ b/src/commands/cmd_gline.cpp
@@ -63,7 +63,7 @@ CmdResult CommandGline::Handle (const std::vector<std::string>& parameters, User
if (ih.first.empty())
{
- user->WriteServ("NOTICE %s :*** Target not found", user->nick.c_str());
+ user->WriteNotice("*** Target not found");
return CMD_FAILURE;
}
@@ -72,11 +72,11 @@ CmdResult CommandGline::Handle (const std::vector<std::string>& parameters, User
else if (target.find('!') != std::string::npos)
{
- user->WriteServ("NOTICE %s :*** G-Line cannot operate on nick!user@host masks",user->nick.c_str());
+ user->WriteNotice("*** G-Line cannot operate on nick!user@host masks");
return CMD_FAILURE;
}
- long duration = ServerInstance->Duration(parameters[1].c_str());
+ unsigned long duration = InspIRCd::Duration(parameters[1]);
GLine* gl = new GLine(ServerInstance->Time(), duration, user->nick.c_str(), parameters[2].c_str(), ih.first.c_str(), ih.second.c_str());
if (ServerInstance->XLines->AddLine(gl, user))
{
@@ -97,7 +97,7 @@ CmdResult CommandGline::Handle (const std::vector<std::string>& parameters, User
else
{
delete gl;
- user->WriteServ("NOTICE %s :*** G-Line for %s already exists",user->nick.c_str(),target.c_str());
+ user->WriteNotice("** G-Line for " + target + " already exists");
}
}
@@ -109,7 +109,7 @@ CmdResult CommandGline::Handle (const std::vector<std::string>& parameters, User
}
else
{
- user->WriteServ("NOTICE %s :*** G-line %s not found in list, try /stats g.",user->nick.c_str(),target.c_str());
+ user->WriteNotice("*** G-Line " + target + " not found in list, try /stats g.");
}
}
diff --git a/src/commands/cmd_hostname_lookup.cpp b/src/commands/cmd_hostname_lookup.cpp
new file mode 100644
index 000000000..f352443d0
--- /dev/null
+++ b/src/commands/cmd_hostname_lookup.cpp
@@ -0,0 +1,239 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2013 Adam <Adam@anope.org>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+#include "modules/dns.h"
+
+namespace
+{
+ LocalIntExt* dl;
+ LocalStringExt* ph;
+}
+
+/** Derived from Resolver, and performs user forward/reverse lookups.
+ */
+class UserResolver : public DNS::Request
+{
+ /** UUID we are looking up */
+ const std::string uuid;
+
+ /** True if the lookup is forward, false if is a reverse lookup
+ */
+ const bool fwd;
+
+ public:
+ /** Create a resolver.
+ * @param mgr DNS Manager
+ * @param me this module
+ * @param user The user to begin lookup on
+ * @param to_resolve The IP or host to resolve
+ * @param qt The query type
+ */
+ UserResolver(DNS::Manager* mgr, Module* me, LocalUser* user, const std::string& to_resolve, DNS::QueryType qt)
+ : DNS::Request(mgr, me, to_resolve, qt)
+ , uuid(user->uuid)
+ , fwd(qt == DNS::QUERY_A || qt == DNS::QUERY_AAAA)
+ {
+ }
+
+ /** Called on successful lookup
+ * if a previous result has already come back.
+ * @param r The finished query
+ */
+ void OnLookupComplete(const DNS::Query* r)
+ {
+ LocalUser* bound_user = (LocalUser*)ServerInstance->FindUUID(uuid);
+ if (!bound_user)
+ {
+ ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Resolution finished for user '%s' who is gone", uuid.c_str());
+ return;
+ }
+
+ const DNS::ResourceRecord& ans_record = r->answers[0];
+
+ ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "DNS result for %s: '%s' -> '%s'", uuid.c_str(), ans_record.name.c_str(), ans_record.rdata.c_str());
+
+ if (!fwd)
+ {
+ // first half of resolution is done. We now need to verify that the host matches.
+ ph->set(bound_user, ans_record.rdata);
+
+ UserResolver* res_forward;
+ if (bound_user->client_sa.sa.sa_family == AF_INET6)
+ {
+ /* IPV6 forward lookup */
+ res_forward = new UserResolver(this->manager, this->creator, bound_user, ans_record.rdata, DNS::QUERY_AAAA);
+ }
+ else
+ {
+ /* IPV4 lookup */
+ res_forward = new UserResolver(this->manager, this->creator, bound_user, ans_record.rdata, DNS::QUERY_A);
+ }
+ try
+ {
+ this->manager->Process(res_forward);
+ }
+ catch (DNS::Exception& e)
+ {
+ delete res_forward;
+ ServerInstance->Logs->Log("RESOLVER", LOG_DEBUG, "Error in resolver: %s",e.GetReason());
+
+ bound_user->WriteNotice("*** There was an internal error resolving your host, using your IP address (" + bound_user->GetIPString() + ") instead.");
+ dl->set(bound_user, 0);
+ }
+ }
+ else
+ {
+ /* Both lookups completed */
+
+ irc::sockets::sockaddrs* user_ip = &bound_user->client_sa;
+ bool rev_match = false;
+ if (user_ip->sa.sa_family == AF_INET6)
+ {
+ struct in6_addr res_bin;
+ if (inet_pton(AF_INET6, ans_record.rdata.c_str(), &res_bin))
+ {
+ rev_match = !memcmp(&user_ip->in6.sin6_addr, &res_bin, sizeof(res_bin));
+ }
+ }
+ else
+ {
+ struct in_addr res_bin;
+ if (inet_pton(AF_INET, ans_record.rdata.c_str(), &res_bin))
+ {
+ rev_match = !memcmp(&user_ip->in4.sin_addr, &res_bin, sizeof(res_bin));
+ }
+ }
+
+ dl->set(bound_user, 0);
+
+ if (rev_match)
+ {
+ std::string* hostname = ph->get(bound_user);
+
+ if (hostname == NULL)
+ {
+ ServerInstance->Logs->Log("RESOLVER", LOG_DEFAULT, "ERROR: User has no hostname attached when doing a forward lookup");
+ bound_user->WriteNotice("*** There was an internal error resolving your host, using your IP address (" + bound_user->GetIPString() + ") instead.");
+ return;
+ }
+ else if (hostname->length() < 65)
+ {
+ /* Hostnames starting with : are not a good thing (tm) */
+ if ((*hostname)[0] == ':')
+ hostname->insert(0, "0");
+
+ bound_user->WriteNotice("*** Found your hostname (" + *hostname + (r->cached ? ") -- cached" : ")"));
+ bound_user->host.assign(*hostname, 0, 64);
+ bound_user->dhost = bound_user->host;
+
+ /* Invalidate cache */
+ bound_user->InvalidateCache();
+ }
+ else
+ {
+ bound_user->WriteNotice("*** Your hostname is longer than the maximum of 64 characters, using your IP address (" + bound_user->GetIPString() + ") instead.");
+ }
+
+ ph->unset(bound_user);
+ }
+ else
+ {
+ bound_user->WriteNotice("*** Your hostname does not match up with your IP address. Sorry, using your IP address (" + bound_user->GetIPString() + ") instead.");
+ }
+ }
+ }
+
+ /** Called on failed lookup
+ * @param query The errored query
+ */
+ void OnError(const DNS::Query* query)
+ {
+ LocalUser* bound_user = (LocalUser*)ServerInstance->FindUUID(uuid);
+ if (bound_user)
+ {
+ bound_user->WriteNotice("*** Could not resolve your hostname: " + this->manager->GetErrorStr(query->error) + "; using your IP address (" + bound_user->GetIPString() + ") instead.");
+ dl->set(bound_user, 0);
+ ServerInstance->stats->statsDnsBad++;
+ }
+ }
+};
+
+class ModuleHostnameLookup : public Module
+{
+ LocalIntExt dnsLookup;
+ LocalStringExt ptrHosts;
+ dynamic_reference<DNS::Manager> DNS;
+
+ public:
+ ModuleHostnameLookup()
+ : dnsLookup("dnsLookup", this)
+ , ptrHosts("ptrHosts", this)
+ , DNS(this, "DNS")
+ {
+ dl = &dnsLookup;
+ ph = &ptrHosts;
+ }
+
+ void init()
+ {
+ ServerInstance->Modules->AddService(this->dnsLookup);
+ ServerInstance->Modules->AddService(this->ptrHosts);
+ }
+
+ void OnUserInit(LocalUser *user)
+ {
+ if (!DNS || !user->MyClass->resolvehostnames)
+ {
+ user->WriteNotice("*** Skipping host resolution (disabled by server administrator)");
+ return;
+ }
+
+ user->WriteNotice("*** Looking up your hostname...");
+
+ UserResolver* res_reverse = new UserResolver(*this->DNS, this, user, user->GetIPString(), DNS::QUERY_PTR);
+ try
+ {
+ /* If both the reverse and forward queries are cached, the user will be able to pass DNS completely
+ * before Process() completes, which is why dnsLookup.set() is here, before Process()
+ */
+ this->dnsLookup.set(user, 1);
+ this->DNS->Process(res_reverse);
+ }
+ catch (DNS::Exception& e)
+ {
+ this->dnsLookup.set(user, 0);
+ delete res_reverse;
+ ServerInstance->Logs->Log("USERS", LOG_DEBUG, "Error in resolver: %s", e.GetReason());
+ ServerInstance->stats->statsDnsBad++;
+ }
+ }
+
+ ModResult OnCheckReady(LocalUser* user)
+ {
+ return this->dnsLookup.get(user) ? MOD_RES_DENY : MOD_RES_PASSTHRU;
+ }
+
+ Version GetVersion()
+ {
+ return Version("Provides support for DNS lookups on connecting clients", VF_CORE|VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleHostnameLookup)
diff --git a/src/commands/cmd_info.cpp b/src/commands/cmd_info.cpp
index 6e5f2a909..b3f146c30 100644
--- a/src/commands/cmd_info.cpp
+++ b/src/commands/cmd_info.cpp
@@ -100,7 +100,7 @@ CmdResult CommandInfo::Handle (const std::vector<std::string>& parameters, User
int i=0;
while (lines[i])
user->SendText(":%s %03d %s :%s", ServerInstance->Config->ServerName.c_str(), RPL_INFO, user->nick.c_str(), lines[i++]);
- FOREACH_MOD(I_OnInfo,OnInfo(user));
+ FOREACH_MOD(OnInfo, (user));
user->SendText(":%s %03d %s :End of /INFO list", ServerInstance->Config->ServerName.c_str(), RPL_ENDOFINFO, user->nick.c_str());
return CMD_SUCCESS;
}
diff --git a/src/commands/cmd_invite.cpp b/src/commands/cmd_invite.cpp
index c69e6bd1b..2ed05c550 100644
--- a/src/commands/cmd_invite.cpp
+++ b/src/commands/cmd_invite.cpp
@@ -40,6 +40,10 @@ class CommandInvite : public Command
* @return A value from CmdResult to indicate command success or failure.
*/
CmdResult Handle(const std::vector<std::string>& parameters, User *user);
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ {
+ return (IS_LOCAL(user) ? ROUTE_LOCALONLY : ROUTE_BROADCAST);
+ }
};
/** Handle /INVITE
@@ -61,7 +65,7 @@ CmdResult CommandInvite::Handle (const std::vector<std::string>& parameters, Use
if (parameters.size() == 3)
{
if (IS_LOCAL(user))
- timeout = ServerInstance->Time() + ServerInstance->Duration(parameters[2]);
+ timeout = ServerInstance->Time() + InspIRCd::Duration(parameters[1]);
else
timeout = ConvToInt(parameters[2]);
}
@@ -107,9 +111,14 @@ CmdResult CommandInvite::Handle (const std::vector<std::string>& parameters, Use
}
if (IS_LOCAL(u))
- IS_LOCAL(u)->InviteTo(c->name.c_str(), timeout);
- u->WriteFrom(user,"INVITE %s :%s",u->nick.c_str(),c->name.c_str());
- user->WriteNumeric(RPL_INVITING, "%s %s %s",user->nick.c_str(),u->nick.c_str(),c->name.c_str());
+ {
+ Invitation::Create(c, IS_LOCAL(u), timeout);
+ u->WriteFrom(user,"INVITE %s :%s",u->nick.c_str(),c->name.c_str());
+ }
+
+ if (IS_LOCAL(user))
+ user->WriteNumeric(RPL_INVITING, "%s %s %s",user->nick.c_str(),u->nick.c_str(),c->name.c_str());
+
if (ServerInstance->Config->AnnounceInvites != ServerConfig::INVITE_ANNOUNCE_NONE)
{
char prefix;
@@ -134,7 +143,7 @@ CmdResult CommandInvite::Handle (const std::vector<std::string>& parameters, Use
}
c->WriteAllExceptSender(user, true, prefix, "NOTICE %s :*** %s invited %s into the channel", c->name.c_str(), user->nick.c_str(), u->nick.c_str());
}
- FOREACH_MOD(I_OnUserInvite,OnUserInvite(user,u,c,timeout));
+ FOREACH_MOD(OnUserInvite, (user,u,c,timeout));
}
else if (IS_LOCAL(user))
{
diff --git a/src/commands/cmd_join.cpp b/src/commands/cmd_join.cpp
index 6124fcc1c..9e2678b5d 100644
--- a/src/commands/cmd_join.cpp
+++ b/src/commands/cmd_join.cpp
@@ -25,44 +25,50 @@
* the same way, however, they can be fully unloaded, where these
* may not.
*/
-class CommandJoin : public Command
+class CommandJoin : public SplitCommand
{
public:
/** Constructor for join.
*/
- CommandJoin ( Module* parent) : Command(parent,"JOIN", 1, 2) { syntax = "<channel>{,<channel>} {<key>{,<key>}}"; Penalty = 2; }
+ CommandJoin(Module* parent)
+ : SplitCommand(parent, "JOIN", 1, 2)
+ {
+ syntax = "<channel>{,<channel>} {<key>{,<key>}}";
+ Penalty = 2;
+ }
+
/** Handle command.
* @param parameters The parameters to the comamnd
* @param pcnt The number of parameters passed to teh command
* @param user The user issuing the command
* @return A value from CmdResult to indicate command success or failure.
*/
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
+ CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser* user);
};
/** Handle /JOIN
*/
-CmdResult CommandJoin::Handle (const std::vector<std::string>& parameters, User *user)
+CmdResult CommandJoin::HandleLocal(const std::vector<std::string>& parameters, LocalUser *user)
{
if (parameters.size() > 1)
{
- if (ServerInstance->Parser->LoopCall(user, this, parameters, 0, 1, false))
+ if (CommandParser::LoopCall(user, this, parameters, 0, 1, false))
return CMD_SUCCESS;
- if (ServerInstance->IsChannel(parameters[0].c_str(), ServerInstance->Config->Limits.ChanMax))
+ if (ServerInstance->IsChannel(parameters[0]))
{
- Channel::JoinUser(user, parameters[0].c_str(), false, parameters[1].c_str(), false);
+ Channel::JoinUser(user, parameters[0], false, parameters[1]);
return CMD_SUCCESS;
}
}
else
{
- if (ServerInstance->Parser->LoopCall(user, this, parameters, 0, -1, false))
+ if (CommandParser::LoopCall(user, this, parameters, 0, -1, false))
return CMD_SUCCESS;
- if (ServerInstance->IsChannel(parameters[0].c_str(), ServerInstance->Config->Limits.ChanMax))
+ if (ServerInstance->IsChannel(parameters[0]))
{
- Channel::JoinUser(user, parameters[0].c_str(), false, "", false);
+ Channel::JoinUser(user, parameters[0]);
return CMD_SUCCESS;
}
}
diff --git a/src/commands/cmd_kick.cpp b/src/commands/cmd_kick.cpp
index 3c5fb0052..b59eec030 100644
--- a/src/commands/cmd_kick.cpp
+++ b/src/commands/cmd_kick.cpp
@@ -38,6 +38,10 @@ class CommandKick : public Command
* @return A value from CmdResult to indicate command success or failure.
*/
CmdResult Handle(const std::vector<std::string>& parameters, User *user);
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ {
+ return (IS_LOCAL(user) ? ROUTE_LOCALONLY : ROUTE_BROADCAST);
+ }
};
/** Handle /KICK
@@ -48,7 +52,7 @@ CmdResult CommandKick::Handle (const std::vector<std::string>& parameters, User
Channel* c = ServerInstance->FindChan(parameters[0]);
User* u;
- if (ServerInstance->Parser->LoopCall(user, this, parameters, 1))
+ if (CommandParser::LoopCall(user, this, parameters, 1))
return CMD_SUCCESS;
if (IS_LOCAL(user))
@@ -62,10 +66,21 @@ CmdResult CommandKick::Handle (const std::vector<std::string>& parameters, User
return CMD_FAILURE;
}
- if ((IS_LOCAL(user)) && (!c->HasUser(user)) && (!ServerInstance->ULine(user->server)))
+ Membership* srcmemb = NULL;
+ if (IS_LOCAL(user))
{
- user->WriteServ( "442 %s %s :You're not on that channel!", user->nick.c_str(), parameters[0].c_str());
- return CMD_FAILURE;
+ srcmemb = c->GetUser(user);
+ if (!srcmemb)
+ {
+ user->WriteServ( "442 %s %s :You're not on that channel!", user->nick.c_str(), parameters[0].c_str());
+ return CMD_FAILURE;
+ }
+
+ if (ServerInstance->ULine(u->server))
+ {
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You may not kick a u-lined client", user->nick.c_str(), c->name.c_str());
+ return CMD_FAILURE;
+ }
}
if (parameters.size() > 2)
@@ -77,7 +92,7 @@ CmdResult CommandKick::Handle (const std::vector<std::string>& parameters, User
reason.assign(user->nick, 0, ServerInstance->Config->Limits.MaxKick);
}
- c->KickUser(user, u, reason.c_str());
+ c->KickUser(user, u, reason, srcmemb);
return CMD_SUCCESS;
}
diff --git a/src/commands/cmd_kill.cpp b/src/commands/cmd_kill.cpp
index 17c8a76a0..dddfe4291 100644
--- a/src/commands/cmd_kill.cpp
+++ b/src/commands/cmd_kill.cpp
@@ -28,13 +28,16 @@
*/
class CommandKill : public Command
{
+ std::string lastuuid;
+ std::string killreason;
+
public:
/** Constructor for kill.
*/
CommandKill ( Module* parent) : Command(parent,"KILL",2,2) {
flags_needed = 'o';
syntax = "<nickname> <reason>";
- TRANSLATE3(TR_NICK, TR_TEXT, TR_END);
+ TRANSLATE2(TR_CUSTOM, TR_CUSTOM);
}
/** Handle command.
* @param parameters The parameters to the comamnd
@@ -45,11 +48,21 @@ class CommandKill : public Command
CmdResult Handle(const std::vector<std::string>& parameters, User *user);
RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
{
- // local kills of remote users are routed via the OnRemoteKill hook
- if (IS_LOCAL(user))
+ // FindNick() doesn't work here because we quit the target user in Handle() which
+ // removes it from the nicklist, so we check lastuuid: if it's empty then this KILL
+ // was for a local user, otherwise it contains the uuid of the user who was killed.
+ if (lastuuid.empty())
return ROUTE_LOCALONLY;
return ROUTE_BROADCAST;
}
+
+ void EncodeParameter(std::string& param, int index)
+ {
+ // Manually translate the nick -> uuid (see above), and also the reason (params[1])
+ // because we decorate it if the oper is local and want remote servers to see the
+ // decorated reason not the original.
+ param = ((index == 0) ? lastuuid : killreason);
+ }
};
/** Handle /KILL
@@ -57,8 +70,12 @@ class CommandKill : public Command
CmdResult CommandKill::Handle (const std::vector<std::string>& parameters, User *user)
{
/* Allow comma seperated lists of users for /KILL (thanks w00t) */
- if (ServerInstance->Parser->LoopCall(user, this, parameters, 0))
- return CMD_SUCCESS;
+ if (CommandParser::LoopCall(user, this, parameters, 0))
+ {
+ // If we got a colon delimited list of nicks then the handler ran for each nick,
+ // and KILL commands were broadcast for remote targets.
+ return CMD_FAILURE;
+ }
User *u = ServerInstance->FindNick(parameters[0]);
if ((u) && (!IS_SERVER(u)))
@@ -71,7 +88,6 @@ CmdResult CommandKill::Handle (const std::vector<std::string>& parameters, User
* just gets processed and passed on, otherwise, if they are local, it gets prefixed. Makes sense :-) -- w00t
*/
- std::string killreason;
if (IS_LOCAL(user))
{
/*
@@ -112,7 +128,7 @@ CmdResult CommandKill::Handle (const std::vector<std::string>& parameters, User
{
// remote kill
ServerInstance->SNO->WriteToSnoMask('K', "Remote kill by %s: %s (%s)", user->nick.c_str(), u->GetFullRealHost().c_str(), parameters[1].c_str());
- FOREACH_MOD(I_OnRemoteKill, OnRemoteKill(user, u, killreason, killreason));
+ this->lastuuid = u->uuid;
}
else
{
@@ -125,7 +141,7 @@ CmdResult CommandKill::Handle (const std::vector<std::string>& parameters, User
ServerInstance->SNO->WriteGlobalSno('k',"Local Kill by %s: %s (%s)", user->nick.c_str(), u->GetFullRealHost().c_str(), parameters[1].c_str());
else
ServerInstance->SNO->WriteToSnoMask('k',"Local Kill by %s: %s (%s)", user->nick.c_str(), u->GetFullRealHost().c_str(), parameters[1].c_str());
- ServerInstance->Logs->Log("KILL",DEFAULT,"LOCAL KILL: %s :%s!%s!%s (%s)", u->nick.c_str(), ServerInstance->Config->ServerName.c_str(), user->dhost.c_str(), user->nick.c_str(), parameters[1].c_str());
+ ServerInstance->Logs->Log("KILL", LOG_DEFAULT, "LOCAL KILL: %s :%s!%s!%s (%s)", u->nick.c_str(), ServerInstance->Config->ServerName.c_str(), user->dhost.c_str(), user->nick.c_str(), parameters[1].c_str());
/* Bug #419, make sure this message can only occur once even in the case of multiple KILL messages crossing the network, and change to show
* hidekillsserver as source if possible
*/
@@ -138,6 +154,8 @@ CmdResult CommandKill::Handle (const std::vector<std::string>& parameters, User
ServerInstance->Config->HideKillsServer.empty() ? user->nick.c_str() : ServerInstance->Config->HideKillsServer.c_str(),
parameters[1].c_str());
}
+
+ this->lastuuid.clear();
}
// send the quit out
diff --git a/src/commands/cmd_kline.cpp b/src/commands/cmd_kline.cpp
index ce3642f91..20afae2a9 100644
--- a/src/commands/cmd_kline.cpp
+++ b/src/commands/cmd_kline.cpp
@@ -63,7 +63,7 @@ CmdResult CommandKline::Handle (const std::vector<std::string>& parameters, User
if (ih.first.empty())
{
- user->WriteServ("NOTICE %s :*** Target not found", user->nick.c_str());
+ user->WriteNotice("*** Target not found");
return CMD_FAILURE;
}
@@ -72,11 +72,11 @@ CmdResult CommandKline::Handle (const std::vector<std::string>& parameters, User
if (target.find('!') != std::string::npos)
{
- user->WriteServ("NOTICE %s :*** K-Line cannot operate on nick!user@host masks",user->nick.c_str());
+ user->WriteNotice("*** K-Line cannot operate on nick!user@host masks");
return CMD_FAILURE;
}
- long duration = ServerInstance->Duration(parameters[1].c_str());
+ unsigned long duration = InspIRCd::Duration(parameters[1]);
KLine* kl = new KLine(ServerInstance->Time(), duration, user->nick.c_str(), parameters[2].c_str(), ih.first.c_str(), ih.second.c_str());
if (ServerInstance->XLines->AddLine(kl,user))
{
@@ -97,7 +97,7 @@ CmdResult CommandKline::Handle (const std::vector<std::string>& parameters, User
else
{
delete kl;
- user->WriteServ("NOTICE %s :*** K-Line for %s already exists",user->nick.c_str(),target.c_str());
+ user->WriteNotice("*** K-Line for " + target + " already exists");
}
}
else
@@ -108,7 +108,7 @@ CmdResult CommandKline::Handle (const std::vector<std::string>& parameters, User
}
else
{
- user->WriteServ("NOTICE %s :*** K-Line %s not found in list, try /stats k.",user->nick.c_str(),target.c_str());
+ user->WriteNotice("*** K-Line " + target + " not found in list, try /stats k.");
}
}
diff --git a/src/commands/cmd_list.cpp b/src/commands/cmd_list.cpp
index 2f417bc04..5962e2547 100644
--- a/src/commands/cmd_list.cpp
+++ b/src/commands/cmd_list.cpp
@@ -27,10 +27,20 @@
*/
class CommandList : public Command
{
+ ChanModeReference secretmode;
+ ChanModeReference privatemode;
+
public:
/** Constructor for list.
*/
- CommandList ( Module* parent) : Command(parent,"LIST", 0, 0) { Penalty = 5; }
+ CommandList(Module* parent)
+ : Command(parent,"LIST", 0, 0)
+ , secretmode(creator, "secret")
+ , privatemode(creator, "private")
+ {
+ Penalty = 5;
+ }
+
/** Handle command.
* @param parameters The parameters to the comamnd
* @param pcnt The number of parameters passed to teh command
@@ -82,14 +92,14 @@ CmdResult CommandList::Handle (const std::vector<std::string>& parameters, User
// if the channel is not private/secret, OR the user is on the channel anyway
bool n = (i->second->HasUser(user) || user->HasPrivPermission("channels/auspex"));
- if (!n && i->second->IsModeSet('p'))
+ if (!n && i->second->IsModeSet(privatemode))
{
/* Channel is +p and user is outside/not privileged */
user->WriteNumeric(322, "%s * %ld :",user->nick.c_str(), users);
}
else
{
- if (n || !i->second->IsModeSet('s'))
+ if (n || !i->second->IsModeSet(secretmode))
{
/* User is in the channel/privileged, channel is not +s */
user->WriteNumeric(322, "%s %s %ld :[+%s] %s",user->nick.c_str(),i->second->name.c_str(),users,i->second->ChanModes(n),i->second->topic.c_str());
diff --git a/src/commands/cmd_lusers.cpp b/src/commands/cmd_lusers.cpp
index 91a718090..06419733d 100644
--- a/src/commands/cmd_lusers.cpp
+++ b/src/commands/cmd_lusers.cpp
@@ -26,10 +26,10 @@ struct LusersCounters
unsigned int max_global;
unsigned int invisible;
- LusersCounters()
+ LusersCounters(unsigned int inv)
: max_local(ServerInstance->Users->LocalUserCount())
, max_global(ServerInstance->Users->RegisteredUserCount())
- , invisible(ServerInstance->Users->ModeCount('i'))
+ , invisible(inv)
{
}
@@ -73,11 +73,11 @@ class CommandLusers : public Command
CmdResult CommandLusers::Handle (const std::vector<std::string>&, User *user)
{
unsigned int n_users = ServerInstance->Users->RegisteredUserCount();
- ProtoServerList serverlist;
+ ProtocolInterface::ServerList serverlist;
ServerInstance->PI->GetServerList(serverlist);
unsigned int n_serv = serverlist.size();
unsigned int n_local_servs = 0;
- for(ProtoServerList::iterator i = serverlist.begin(); i != serverlist.end(); ++i)
+ for (ProtocolInterface::ServerList::const_iterator i = serverlist.begin(); i != serverlist.end(); ++i)
{
if (i->parentname == ServerInstance->Config->ServerName)
n_local_servs++;
@@ -110,11 +110,11 @@ class InvisibleWatcher : public ModeWatcher
unsigned int& invisible;
public:
InvisibleWatcher(Module* mod, unsigned int& Invisible)
- : ModeWatcher(mod, 'i', MODETYPE_USER), invisible(Invisible)
+ : ModeWatcher(mod, "invisible", MODETYPE_USER), invisible(Invisible)
{
}
- void AfterMode(User* source, User* dest, Channel* channel, const std::string& parameter, bool adding, ModeType type)
+ void AfterMode(User* source, User* dest, Channel* channel, const std::string& parameter, bool adding)
{
if (dest->registered != REG_ALL)
return;
@@ -128,34 +128,48 @@ public:
class ModuleLusers : public Module
{
+ UserModeReference invisiblemode;
LusersCounters counters;
CommandLusers cmd;
InvisibleWatcher mw;
+ unsigned int CountInvisible()
+ {
+ unsigned int c = 0;
+ for (user_hash::iterator i = ServerInstance->Users->clientlist->begin(); i != ServerInstance->Users->clientlist->end(); ++i)
+ {
+ User* u = i->second;
+ if (u->IsModeSet(invisiblemode))
+ c++;
+ }
+ return c;
+ }
+
public:
ModuleLusers()
- : cmd(this, counters), mw(this, counters.invisible)
+ : invisiblemode(this, "invisible")
+ , counters(CountInvisible())
+ , cmd(this, counters)
+ , mw(this, counters.invisible)
{
}
void init()
{
ServerInstance->Modules->AddService(cmd);
- Implementation events[] = { I_OnPostConnect, I_OnUserQuit };
- ServerInstance->Modules->Attach(events, this, sizeof(events)/sizeof(Implementation));
ServerInstance->Modes->AddModeWatcher(&mw);
}
void OnPostConnect(User* user)
{
counters.UpdateMaxUsers();
- if (user->IsModeSet('i'))
+ if (user->IsModeSet(invisiblemode))
counters.invisible++;
}
void OnUserQuit(User* user, const std::string& message, const std::string& oper_message)
{
- if (user->IsModeSet('i'))
+ if (user->IsModeSet(invisiblemode))
counters.invisible--;
}
diff --git a/src/commands/cmd_map.cpp b/src/commands/cmd_map.cpp
deleted file mode 100644
index 385a2c752..000000000
--- a/src/commands/cmd_map.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-
-class CommandMap : public Command
-{
- public:
- /** Constructor for map.
- */
- CommandMap ( Module* parent) : Command(parent,"MAP",0,0) { Penalty=2; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-};
-
-/** Handle /MAP
- */
-CmdResult CommandMap::Handle (const std::vector<std::string>&, User *user)
-{
- // as with /LUSERS this does nothing without a linking
- // module to override its behaviour and display something
- // better.
-
- if (IS_OPER(user))
- {
- user->WriteNumeric(006, "%s :%s [%s]", user->nick.c_str(), ServerInstance->Config->ServerName.c_str(), ServerInstance->Config->GetSID().c_str());
- user->WriteNumeric(007, "%s :End of /MAP", user->nick.c_str());
- return CMD_SUCCESS;
- }
- user->WriteNumeric(006, "%s :%s",user->nick.c_str(),ServerInstance->Config->ServerName.c_str());
- user->WriteNumeric(007, "%s :End of /MAP",user->nick.c_str());
-
- return CMD_SUCCESS;
-}
-
-COMMAND_INIT(CommandMap)
diff --git a/src/commands/cmd_mode.cpp b/src/commands/cmd_mode.cpp
index 17e21b182..1a42835e5 100644
--- a/src/commands/cmd_mode.cpp
+++ b/src/commands/cmd_mode.cpp
@@ -38,6 +38,10 @@ class CommandMode : public Command
* @return A value from CmdResult to indicate command success or failure.
*/
CmdResult Handle(const std::vector<std::string>& parameters, User *user);
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ {
+ return (IS_LOCAL(user) ? ROUTE_LOCALONLY : ROUTE_BROADCAST);
+ }
};
@@ -45,7 +49,7 @@ class CommandMode : public Command
*/
CmdResult CommandMode::Handle (const std::vector<std::string>& parameters, User *user)
{
- ServerInstance->Modes->Process(parameters, user, false);
+ ServerInstance->Modes->Process(parameters, user, (IS_LOCAL(user) ? ModeParser::MODE_NONE : ModeParser::MODE_LOCALONLY));
return CMD_SUCCESS;
}
diff --git a/src/commands/cmd_motd.cpp b/src/commands/cmd_motd.cpp
index 8e227723e..b28e57b2f 100644
--- a/src/commands/cmd_motd.cpp
+++ b/src/commands/cmd_motd.cpp
@@ -54,8 +54,9 @@ CmdResult CommandMotd::Handle (const std::vector<std::string>& parameters, User
return CMD_SUCCESS;
ConfigTag* tag = NULL;
- if (IS_LOCAL(user))
- tag = user->GetClass()->config;
+ LocalUser* localuser = IS_LOCAL(user);
+ if (localuser)
+ tag = localuser->GetClass()->config;
std::string motd_name = tag->getString("motd", "motd");
ConfigFileCache::iterator motd = ServerInstance->Config->Files.find(motd_name);
if (motd == ServerInstance->Config->Files.end())
diff --git a/src/commands/cmd_names.cpp b/src/commands/cmd_names.cpp
index 0c06b636f..c74d18c23 100644
--- a/src/commands/cmd_names.cpp
+++ b/src/commands/cmd_names.cpp
@@ -27,10 +27,18 @@
*/
class CommandNames : public Command
{
+ ChanModeReference secretmode;
+
public:
/** Constructor for names.
*/
- CommandNames ( Module* parent) : Command(parent,"NAMES",0,0) { syntax = "{<channel>{,<channel>}}"; }
+ CommandNames(Module* parent)
+ : Command(parent, "NAMES", 0, 0)
+ , secretmode(parent, "secret")
+ {
+ syntax = "{<channel>{,<channel>}}";
+ }
+
/** Handle command.
* @param parameters The parameters to the comamnd
* @param pcnt The number of parameters passed to teh command
@@ -52,13 +60,13 @@ CmdResult CommandNames::Handle (const std::vector<std::string>& parameters, User
return CMD_SUCCESS;
}
- if (ServerInstance->Parser->LoopCall(user, this, parameters, 0))
+ if (CommandParser::LoopCall(user, this, parameters, 0))
return CMD_SUCCESS;
c = ServerInstance->FindChan(parameters[0]);
if (c)
{
- if ((c->IsModeSet('s')) && (!c->HasUser(user)))
+ if ((c->IsModeSet(secretmode)) && (!c->HasUser(user)))
{
user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), c->name.c_str());
return CMD_FAILURE;
diff --git a/src/commands/cmd_nick.cpp b/src/commands/cmd_nick.cpp
index a079e59d0..e58aab986 100644
--- a/src/commands/cmd_nick.cpp
+++ b/src/commands/cmd_nick.cpp
@@ -66,7 +66,7 @@ CmdResult CommandNick::Handle (const std::vector<std::string>& parameters, User
{
newnick = user->uuid;
}
- else if (!ServerInstance->IsNick(newnick.c_str(), ServerInstance->Config->Limits.NickMax))
+ else if (!ServerInstance->IsNick(newnick))
{
user->WriteNumeric(432, "%s %s :Erroneous Nickname", user->nick.c_str(),newnick.c_str());
return CMD_FAILURE;
diff --git a/src/commands/cmd_notice.cpp b/src/commands/cmd_notice.cpp
deleted file mode 100644
index d5ef7ba1d..000000000
--- a/src/commands/cmd_notice.cpp
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * InspIRCd -- Internet Relay Chat Daemon
- *
- * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
- *
- * This file is part of InspIRCd. InspIRCd is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General Public
- * License as published by the Free Software Foundation, version 2.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "inspircd.h"
-/** Handle /NOTICE. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandNotice : public Command
-{
- public:
- /** Constructor for notice.
- */
- CommandNotice ( Module* parent) : Command(parent,"NOTICE",2,2) { syntax = "<target>{,<target>} <message>"; }
- /** Handle command.
- * @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
- * @param user The user issuing the command
- * @return A value from CmdResult to indicate command success or failure.
- */
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-
- RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
- {
- if (IS_LOCAL(user))
- // This is handled by the OnUserNotice hook to split the LoopCall pieces
- return ROUTE_LOCALONLY;
- else
- return ROUTE_MESSAGE(parameters[0]);
- }
-};
-
-
-CmdResult CommandNotice::Handle (const std::vector<std::string>& parameters, User *user)
-{
- User *dest;
- Channel *chan;
-
- CUList exempt_list;
-
- user->idle_lastmsg = ServerInstance->Time();
-
- if (ServerInstance->Parser->LoopCall(user, this, parameters, 0))
- return CMD_SUCCESS;
- if (parameters[0][0] == '$')
- {
- if (!user->HasPrivPermission("users/mass-message"))
- return CMD_SUCCESS;
-
- ModResult MOD_RESULT;
- std::string temp = parameters[1];
- FIRST_MOD_RESULT(OnUserPreNotice, MOD_RESULT, (user, (void*)parameters[0].c_str(), TYPE_SERVER, temp, 0, exempt_list));
- if (MOD_RESULT == MOD_RES_DENY)
- return CMD_FAILURE;
- const char* text = temp.c_str();
- const char* servermask = (parameters[0].c_str()) + 1;
-
- FOREACH_MOD(I_OnText,OnText(user, (void*)parameters[0].c_str(), TYPE_SERVER, text, 0, exempt_list));
- if (InspIRCd::Match(ServerInstance->Config->ServerName,servermask, NULL))
- {
- user->SendAll("NOTICE", "%s", text);
- }
- FOREACH_MOD(I_OnUserNotice,OnUserNotice(user, (void*)parameters[0].c_str(), TYPE_SERVER, text, 0, exempt_list));
- return CMD_SUCCESS;
- }
- char status = 0;
- const char* target = parameters[0].c_str();
-
- if (ServerInstance->Modes->FindPrefix(*target))
- {
- status = *target;
- target++;
- }
- if (*target == '#')
- {
- chan = ServerInstance->FindChan(target);
-
- exempt_list.insert(user);
-
- if (chan)
- {
- if (IS_LOCAL(user))
- {
- if ((chan->IsModeSet('n')) && (!chan->HasUser(user)))
- {
- user->WriteNumeric(404, "%s %s :Cannot send to channel (no external messages)", user->nick.c_str(), chan->name.c_str());
- return CMD_FAILURE;
- }
- if ((chan->IsModeSet('m')) && (chan->GetPrefixValue(user) < VOICE_VALUE))
- {
- user->WriteNumeric(404, "%s %s :Cannot send to channel (+m)", user->nick.c_str(), chan->name.c_str());
- return CMD_FAILURE;
- }
-
- if (ServerInstance->Config->RestrictBannedUsers)
- {
- if (chan->IsBanned(user))
- {
- user->WriteNumeric(404, "%s %s :Cannot send to channel (you're banned)", user->nick.c_str(), chan->name.c_str());
- return CMD_FAILURE;
- }
- }
- }
- ModResult MOD_RESULT;
-
- std::string temp = parameters[1];
- FIRST_MOD_RESULT(OnUserPreNotice, MOD_RESULT, (user,chan,TYPE_CHANNEL,temp,status, exempt_list));
- if (MOD_RESULT == MOD_RES_DENY)
- return CMD_FAILURE;
-
- const char* text = temp.c_str();
-
- if (temp.empty())
- {
- user->WriteNumeric(412, "%s :No text to send", user->nick.c_str());
- return CMD_FAILURE;
- }
-
- FOREACH_MOD(I_OnText,OnText(user,chan,TYPE_CHANNEL,text,status,exempt_list));
-
- if (status)
- {
- if (ServerInstance->Config->UndernetMsgPrefix)
- {
- chan->WriteAllExcept(user, false, status, exempt_list, "NOTICE %c%s :%c %s", status, chan->name.c_str(), status, text);
- }
- else
- {
- chan->WriteAllExcept(user, false, status, exempt_list, "NOTICE %c%s :%s", status, chan->name.c_str(), text);
- }
- }
- else
- {
- chan->WriteAllExcept(user, false, status, exempt_list, "NOTICE %s :%s", chan->name.c_str(), text);
- }
-
- FOREACH_MOD(I_OnUserNotice,OnUserNotice(user,chan,TYPE_CHANNEL,text,status,exempt_list));
- }
- else
- {
- /* no such nick/channel */
- user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), target);
- return CMD_FAILURE;
- }
- return CMD_SUCCESS;
- }
-
- const char* destnick = parameters[0].c_str();
-
- if (IS_LOCAL(user))
- {
- const char* targetserver = strchr(destnick, '@');
-
- if (targetserver)
- {
- std::string nickonly;
-
- nickonly.assign(destnick, 0, targetserver - destnick);
- dest = ServerInstance->FindNickOnly(nickonly);
- if (dest && strcasecmp(dest->server.c_str(), targetserver + 1))
- {
- /* Incorrect server for user */
- user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), parameters[0].c_str());
- return CMD_FAILURE;
- }
- }
- else
- dest = ServerInstance->FindNickOnly(destnick);
- }
- else
- dest = ServerInstance->FindNick(destnick);
-
- if ((dest) && (dest->registered == REG_ALL))
- {
- if (parameters[1].empty())
- {
- user->WriteNumeric(412, "%s :No text to send", user->nick.c_str());
- return CMD_FAILURE;
- }
-
- ModResult MOD_RESULT;
- std::string temp = parameters[1];
- FIRST_MOD_RESULT(OnUserPreNotice, MOD_RESULT, (user,dest,TYPE_USER,temp,0,exempt_list));
- if (MOD_RESULT == MOD_RES_DENY) {
- return CMD_FAILURE;
- }
- const char* text = temp.c_str();
-
- FOREACH_MOD(I_OnText,OnText(user,dest,TYPE_USER,text,0,exempt_list));
-
- if (IS_LOCAL(dest))
- {
- // direct write, same server
- user->WriteTo(dest, "NOTICE %s :%s", dest->nick.c_str(), text);
- }
-
- FOREACH_MOD(I_OnUserNotice,OnUserNotice(user,dest,TYPE_USER,text,0,exempt_list));
- }
- else
- {
- /* no such nick/channel */
- user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), parameters[0].c_str());
- return CMD_FAILURE;
- }
-
- return CMD_SUCCESS;
-
-}
-
-COMMAND_INIT(CommandNotice)
diff --git a/src/commands/cmd_oper.cpp b/src/commands/cmd_oper.cpp
index 1a5e7e178..e1018f805 100644
--- a/src/commands/cmd_oper.cpp
+++ b/src/commands/cmd_oper.cpp
@@ -21,8 +21,6 @@
#include "inspircd.h"
-bool OneOfMatches(const char* host, const char* ip, const char* hostlist);
-
/** Handle /OPER. These command handlers can be reloaded by the core,
* and handle basic RFC1459 commands. Commands within modules work
* the same way, however, they can be fully unloaded, where these
@@ -43,30 +41,14 @@ class CommandOper : public SplitCommand
CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser *user);
};
-bool OneOfMatches(const char* host, const char* ip, const std::string& hostlist)
-{
- std::stringstream hl(hostlist);
- std::string xhost;
- while (hl >> xhost)
- {
- if (InspIRCd::Match(host, xhost, ascii_case_insensitive_map) || InspIRCd::MatchCIDR(ip, xhost, ascii_case_insensitive_map))
- {
- return true;
- }
- }
- return false;
-}
-
CmdResult CommandOper::HandleLocal(const std::vector<std::string>& parameters, LocalUser *user)
{
- char TheHost[MAXBUF];
- char TheIP[MAXBUF];
bool match_login = false;
bool match_pass = false;
bool match_hosts = false;
- snprintf(TheHost,MAXBUF,"%s@%s",user->ident.c_str(),user->host.c_str());
- snprintf(TheIP, MAXBUF,"%s@%s",user->ident.c_str(),user->GetIPString());
+ const std::string userHost = user->ident + "@" + user->host;
+ const std::string userIP = user->ident + "@" + user->GetIPString();
OperIndex::iterator i = ServerInstance->Config->oper_blocks.find(parameters[0]);
if (i != ServerInstance->Config->oper_blocks.end())
@@ -75,7 +57,7 @@ CmdResult CommandOper::HandleLocal(const std::vector<std::string>& parameters, L
ConfigTag* tag = ifo->oper_block;
match_login = true;
match_pass = !ServerInstance->PassCompare(user, tag->getString("password"), parameters[1], tag->getString("hash"));
- match_hosts = OneOfMatches(TheHost,TheIP,tag->getString("host"));
+ match_hosts = InspIRCd::MatchMask(tag->getString("host"), userHost, userIP);
if (match_pass && match_hosts)
{
@@ -98,7 +80,7 @@ CmdResult CommandOper::HandleLocal(const std::vector<std::string>& parameters, L
user->CommandFloodPenalty += 10000;
ServerInstance->SNO->WriteGlobalSno('o', "WARNING! Failed oper attempt by %s using login '%s': The following fields do not match: %s", user->GetFullRealHost().c_str(), parameters[0].c_str(), fields.c_str());
- ServerInstance->Logs->Log("OPER",DEFAULT,"OPER: Failed oper attempt by %s using login '%s': The following fields did not match: %s", user->GetFullRealHost().c_str(), parameters[0].c_str(), fields.c_str());
+ ServerInstance->Logs->Log("OPER", LOG_DEFAULT, "OPER: Failed oper attempt by %s using login '%s': The following fields did not match: %s", user->GetFullRealHost().c_str(), parameters[0].c_str(), fields.c_str());
return CMD_FAILURE;
}
diff --git a/src/commands/cmd_part.cpp b/src/commands/cmd_part.cpp
index aadb42d90..adf9da727 100644
--- a/src/commands/cmd_part.cpp
+++ b/src/commands/cmd_part.cpp
@@ -38,6 +38,10 @@ class CommandPart : public Command
* @return A value from CmdResult to indicate command success or failure.
*/
CmdResult Handle(const std::vector<std::string>& parameters, User *user);
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ {
+ return (IS_LOCAL(user) ? ROUTE_LOCALONLY : ROUTE_BROADCAST);
+ }
};
CmdResult CommandPart::Handle (const std::vector<std::string>& parameters, User *user)
@@ -57,7 +61,7 @@ CmdResult CommandPart::Handle (const std::vector<std::string>& parameters, User
reason = parameters[1];
}
- if (ServerInstance->Parser->LoopCall(user, this, parameters, 0))
+ if (CommandParser::LoopCall(user, this, parameters, 0))
return CMD_SUCCESS;
Channel* c = ServerInstance->FindChan(parameters[0]);
diff --git a/src/commands/cmd_privmsg.cpp b/src/commands/cmd_privmsg.cpp
index cefdd4800..ed98c771f 100644
--- a/src/commands/cmd_privmsg.cpp
+++ b/src/commands/cmd_privmsg.cpp
@@ -21,24 +21,31 @@
#include "inspircd.h"
-/** Handle /PRIVMSG. These command handlers can be reloaded by the core,
- * and handle basic RFC1459 commands. Commands within modules work
- * the same way, however, they can be fully unloaded, where these
- * may not.
- */
-class CommandPrivmsg : public Command
+namespace
+{
+ const char* MessageTypeString[] = { "PRIVMSG", "NOTICE" };
+}
+
+class MessageCommandBase : public Command
{
+ ChanModeReference moderatedmode;
+ ChanModeReference noextmsgmode;
+
public:
- /** Constructor for privmsg.
- */
- CommandPrivmsg ( Module* parent) : Command(parent,"PRIVMSG",2,2) { syntax = "<target>{,<target>} <message>"; }
+ MessageCommandBase(Module* parent, MessageType mt)
+ : Command(parent, MessageTypeString[mt], 2, 2)
+ , moderatedmode(parent, "moderated")
+ , noextmsgmode(parent, "noextmsg")
+ {
+ syntax = "<target>{,<target>} <message>";
+ }
+
/** Handle command.
* @param parameters The parameters to the comamnd
- * @param pcnt The number of parameters passed to teh command
* @param user The user issuing the command
* @return A value from CmdResult to indicate command success or failure.
*/
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
+ CmdResult HandleMessage(const std::vector<std::string>& parameters, User* user, MessageType mt);
RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
{
@@ -50,15 +57,17 @@ class CommandPrivmsg : public Command
}
};
-CmdResult CommandPrivmsg::Handle (const std::vector<std::string>& parameters, User *user)
+CmdResult MessageCommandBase::HandleMessage(const std::vector<std::string>& parameters, User* user, MessageType mt)
{
User *dest;
Channel *chan;
CUList except_list;
- user->idle_lastmsg = ServerInstance->Time();
+ LocalUser* localuser = IS_LOCAL(user);
+ if (localuser)
+ localuser->idle_lastmsg = ServerInstance->Time();
- if (ServerInstance->Parser->LoopCall(user, this, parameters, 0))
+ if (CommandParser::LoopCall(user, this, parameters, 0))
return CMD_SUCCESS;
if (parameters[0][0] == '$')
@@ -68,19 +77,19 @@ CmdResult CommandPrivmsg::Handle (const std::vector<std::string>& parameters, Us
ModResult MOD_RESULT;
std::string temp = parameters[1];
- FIRST_MOD_RESULT(OnUserPreMessage, MOD_RESULT, (user, (void*)parameters[0].c_str(), TYPE_SERVER, temp, 0, except_list));
+ FIRST_MOD_RESULT(OnUserPreMessage, MOD_RESULT, (user, (void*)parameters[0].c_str(), TYPE_SERVER, temp, 0, except_list, mt));
if (MOD_RESULT == MOD_RES_DENY)
return CMD_FAILURE;
const char* text = temp.c_str();
const char* servermask = (parameters[0].c_str()) + 1;
- FOREACH_MOD(I_OnText,OnText(user, (void*)parameters[0].c_str(), TYPE_SERVER, text, 0, except_list));
+ FOREACH_MOD(OnText, (user, (void*)parameters[0].c_str(), TYPE_SERVER, text, 0, except_list));
if (InspIRCd::Match(ServerInstance->Config->ServerName, servermask, NULL))
{
- user->SendAll("PRIVMSG", "%s", text);
+ user->SendAll(MessageTypeString[mt], "%s", text);
}
- FOREACH_MOD(I_OnUserMessage,OnUserMessage(user, (void*)parameters[0].c_str(), TYPE_SERVER, text, 0, except_list));
+ FOREACH_MOD(OnUserMessage, (user, (void*)parameters[0].c_str(), TYPE_SERVER, text, 0, except_list, mt));
return CMD_SUCCESS;
}
char status = 0;
@@ -99,15 +108,15 @@ CmdResult CommandPrivmsg::Handle (const std::vector<std::string>& parameters, Us
if (chan)
{
- if (IS_LOCAL(user) && chan->GetPrefixValue(user) < VOICE_VALUE)
+ if (localuser && chan->GetPrefixValue(user) < VOICE_VALUE)
{
- if (chan->IsModeSet('n') && !chan->HasUser(user))
+ if (chan->IsModeSet(noextmsgmode) && !chan->HasUser(user))
{
user->WriteNumeric(404, "%s %s :Cannot send to channel (no external messages)", user->nick.c_str(), chan->name.c_str());
return CMD_FAILURE;
}
- if (chan->IsModeSet('m'))
+ if (chan->IsModeSet(moderatedmode))
{
user->WriteNumeric(404, "%s %s :Cannot send to channel (+m)", user->nick.c_str(), chan->name.c_str());
return CMD_FAILURE;
@@ -125,7 +134,7 @@ CmdResult CommandPrivmsg::Handle (const std::vector<std::string>& parameters, Us
ModResult MOD_RESULT;
std::string temp = parameters[1];
- FIRST_MOD_RESULT(OnUserPreMessage, MOD_RESULT, (user,chan,TYPE_CHANNEL,temp,status,except_list));
+ FIRST_MOD_RESULT(OnUserPreMessage, MOD_RESULT, (user, chan, TYPE_CHANNEL, temp, status, except_list, mt));
if (MOD_RESULT == MOD_RES_DENY)
return CMD_FAILURE;
@@ -138,25 +147,25 @@ CmdResult CommandPrivmsg::Handle (const std::vector<std::string>& parameters, Us
return CMD_FAILURE;
}
- FOREACH_MOD(I_OnText,OnText(user,chan,TYPE_CHANNEL,text,status,except_list));
+ FOREACH_MOD(OnText, (user,chan,TYPE_CHANNEL,text,status,except_list));
if (status)
{
if (ServerInstance->Config->UndernetMsgPrefix)
{
- chan->WriteAllExcept(user, false, status, except_list, "PRIVMSG %c%s :%c %s", status, chan->name.c_str(), status, text);
+ chan->WriteAllExcept(user, false, status, except_list, "%s %c%s :%c %s", MessageTypeString[mt], status, chan->name.c_str(), status, text);
}
else
{
- chan->WriteAllExcept(user, false, status, except_list, "PRIVMSG %c%s :%s", status, chan->name.c_str(), text);
+ chan->WriteAllExcept(user, false, status, except_list, "%s %c%s :%s", MessageTypeString[mt], status, chan->name.c_str(), text);
}
}
else
{
- chan->WriteAllExcept(user, false, status, except_list, "PRIVMSG %s :%s", chan->name.c_str(), text);
+ chan->WriteAllExcept(user, false, status, except_list, "%s %s :%s", MessageTypeString[mt], chan->name.c_str(), text);
}
- FOREACH_MOD(I_OnUserMessage,OnUserMessage(user,chan,TYPE_CHANNEL,text,status,except_list));
+ FOREACH_MOD(OnUserMessage, (user,chan, TYPE_CHANNEL, text, status, except_list, mt));
}
else
{
@@ -169,7 +178,7 @@ CmdResult CommandPrivmsg::Handle (const std::vector<std::string>& parameters, Us
const char* destnick = parameters[0].c_str();
- if (IS_LOCAL(user))
+ if (localuser)
{
const char* targetserver = strchr(destnick, '@');
@@ -200,7 +209,7 @@ CmdResult CommandPrivmsg::Handle (const std::vector<std::string>& parameters, Us
return CMD_FAILURE;
}
- if (IS_AWAY(dest))
+ if ((dest->IsAway()) && (mt == MSG_PRIVMSG))
{
/* auto respond with aweh msg */
user->WriteNumeric(301, "%s %s :%s", user->nick.c_str(), dest->nick.c_str(), dest->awaymsg.c_str());
@@ -209,21 +218,21 @@ CmdResult CommandPrivmsg::Handle (const std::vector<std::string>& parameters, Us
ModResult MOD_RESULT;
std::string temp = parameters[1];
- FIRST_MOD_RESULT(OnUserPreMessage, MOD_RESULT, (user, dest, TYPE_USER, temp, 0, except_list));
+ FIRST_MOD_RESULT(OnUserPreMessage, MOD_RESULT, (user, dest, TYPE_USER, temp, 0, except_list, mt));
if (MOD_RESULT == MOD_RES_DENY)
return CMD_FAILURE;
const char* text = temp.c_str();
- FOREACH_MOD(I_OnText,OnText(user, dest, TYPE_USER, text, 0, except_list));
+ FOREACH_MOD(OnText, (user, dest, TYPE_USER, text, 0, except_list));
if (IS_LOCAL(dest))
{
// direct write, same server
- user->WriteTo(dest, "PRIVMSG %s :%s", dest->nick.c_str(), text);
+ user->WriteTo(dest, "%s %s :%s", MessageTypeString[mt], dest->nick.c_str(), text);
}
- FOREACH_MOD(I_OnUserMessage,OnUserMessage(user, dest, TYPE_USER, text, 0, except_list));
+ FOREACH_MOD(OnUserMessage, (user, dest, TYPE_USER, text, 0, except_list, mt));
}
else
{
@@ -234,4 +243,42 @@ CmdResult CommandPrivmsg::Handle (const std::vector<std::string>& parameters, Us
return CMD_SUCCESS;
}
-COMMAND_INIT(CommandPrivmsg)
+template<MessageType MT>
+class CommandMessage : public MessageCommandBase
+{
+ public:
+ CommandMessage(Module* parent)
+ : MessageCommandBase(parent, MT)
+ {
+ }
+
+ CmdResult Handle(const std::vector<std::string>& parameters, User* user)
+ {
+ return HandleMessage(parameters, user, MT);
+ }
+};
+
+class ModuleCoreMessage : public Module
+{
+ CommandMessage<MSG_PRIVMSG> CommandPrivmsg;
+ CommandMessage<MSG_NOTICE> CommandNotice;
+
+ public:
+ ModuleCoreMessage()
+ : CommandPrivmsg(this), CommandNotice(this)
+ {
+ }
+
+ void init()
+ {
+ ServerInstance->Modules->AddService(CommandPrivmsg);
+ ServerInstance->Modules->AddService(CommandNotice);
+ }
+
+ Version GetVersion()
+ {
+ return Version("PRIVMSG, NOTICE", VF_CORE|VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleCoreMessage)
diff --git a/src/commands/cmd_qline.cpp b/src/commands/cmd_qline.cpp
index 3118798e6..bfc9e4519 100644
--- a/src/commands/cmd_qline.cpp
+++ b/src/commands/cmd_qline.cpp
@@ -48,11 +48,11 @@ CmdResult CommandQline::Handle (const std::vector<std::string>& parameters, User
if (parameters[0].find('@') != std::string::npos || parameters[0].find('!') != std::string::npos || parameters[0].find('.') != std::string::npos)
{
- user->WriteServ("NOTICE %s :*** A Q-Line only bans a nick pattern, not a nick!user@host pattern.",user->nick.c_str());
+ user->WriteNotice("*** A Q-Line only bans a nick pattern, not a nick!user@host pattern.");
return CMD_FAILURE;
}
- long duration = ServerInstance->Duration(parameters[1].c_str());
+ unsigned long duration = InspIRCd::Duration(parameters[1]);
QLine* ql = new QLine(ServerInstance->Time(), duration, user->nick.c_str(), parameters[2].c_str(), parameters[0].c_str());
if (ServerInstance->XLines->AddLine(ql,user))
{
@@ -72,7 +72,7 @@ CmdResult CommandQline::Handle (const std::vector<std::string>& parameters, User
else
{
delete ql;
- user->WriteServ("NOTICE %s :*** Q-Line for %s already exists",user->nick.c_str(),parameters[0].c_str());
+ user->WriteNotice("*** Q-Line for " + parameters[0] + " already exists");
}
}
else
@@ -83,7 +83,7 @@ CmdResult CommandQline::Handle (const std::vector<std::string>& parameters, User
}
else
{
- user->WriteServ("NOTICE %s :*** Q-Line %s not found in list, try /stats q.",user->nick.c_str(),parameters[0].c_str());
+ user->WriteNotice("*** Q-Line " + parameters[0] + " not found in list, try /stats q.");
return CMD_FAILURE;
}
}
diff --git a/src/commands/cmd_quit.cpp b/src/commands/cmd_quit.cpp
index 6a6b447e5..61a88e2b5 100644
--- a/src/commands/cmd_quit.cpp
+++ b/src/commands/cmd_quit.cpp
@@ -38,6 +38,10 @@ class CommandQuit : public Command
* @return A value from CmdResult to indicate command success or failure.
*/
CmdResult Handle(const std::vector<std::string>& parameters, User *user);
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ {
+ return (IS_LOCAL(user) ? ROUTE_LOCALONLY : ROUTE_BROADCAST);
+ }
};
diff --git a/src/commands/cmd_rehash.cpp b/src/commands/cmd_rehash.cpp
index abf0b7876..3dc454036 100644
--- a/src/commands/cmd_rehash.cpp
+++ b/src/commands/cmd_rehash.cpp
@@ -45,7 +45,7 @@ CmdResult CommandRehash::Handle (const std::vector<std::string>& parameters, Use
{
std::string param = parameters.size() ? parameters[0] : "";
- FOREACH_MOD(I_OnPreRehash,OnPreRehash(user, param));
+ FOREACH_MOD(OnPreRehash, (user, param));
if (param.empty())
{
@@ -68,7 +68,7 @@ CmdResult CommandRehash::Handle (const std::vector<std::string>& parameters, Use
if (param[0] == '-')
param = param.substr(1);
- FOREACH_MOD(I_OnModuleRehash,OnModuleRehash(user, param));
+ FOREACH_MOD(OnModuleRehash, (user, param));
return CMD_SUCCESS;
}
@@ -88,8 +88,7 @@ CmdResult CommandRehash::Handle (const std::vector<std::string>& parameters, Use
/* Don't do anything with the logs here -- logs are restarted
* after the config thread has completed.
*/
- ServerInstance->RehashUsersAndChans();
- FOREACH_MOD(I_OnGarbageCollect, OnGarbageCollect());
+ FOREACH_MOD(OnGarbageCollect, ());
ServerInstance->ConfigThread = new ConfigReaderThread(user->uuid);
@@ -102,7 +101,7 @@ CmdResult CommandRehash::Handle (const std::vector<std::string>& parameters, Use
* XXX, todo: we should find some way to kill runaway rehashes that are blocking, this is a major problem for unrealircd users
*/
if (IS_LOCAL(user))
- user->WriteServ("NOTICE %s :*** Could not rehash: A rehash is already in progress.", user->nick.c_str());
+ user->WriteNotice("*** Could not rehash: A rehash is already in progress.");
else
ServerInstance->PI->SendUserNotice(user, "*** Could not rehash: A rehash is already in progress.");
}
diff --git a/src/commands/cmd_restart.cpp b/src/commands/cmd_restart.cpp
index bdbcfed35..6711ada7d 100644
--- a/src/commands/cmd_restart.cpp
+++ b/src/commands/cmd_restart.cpp
@@ -39,7 +39,7 @@ class CommandRestart : public Command
CmdResult CommandRestart::Handle (const std::vector<std::string>& parameters, User *user)
{
- ServerInstance->Logs->Log("COMMAND",DEFAULT,"Restart: %s",user->nick.c_str());
+ ServerInstance->Logs->Log("COMMAND", LOG_DEFAULT, "Restart: %s",user->nick.c_str());
if (!ServerInstance->PassCompare(user, ServerInstance->Config->restartpass, parameters[0].c_str(), ServerInstance->Config->powerhash))
{
ServerInstance->SNO->WriteGlobalSno('a', "RESTART command from %s, restarting server.", user->GetFullRealHost().c_str());
diff --git a/src/commands/cmd_rules.cpp b/src/commands/cmd_rules.cpp
index 5d41aa4b8..76ee0061b 100644
--- a/src/commands/cmd_rules.cpp
+++ b/src/commands/cmd_rules.cpp
@@ -52,8 +52,9 @@ CmdResult CommandRules::Handle (const std::vector<std::string>& parameters, User
return CMD_SUCCESS;
ConfigTag* tag = NULL;
- if (IS_LOCAL(user))
- tag = user->GetClass()->config;
+ LocalUser* localuser = IS_LOCAL(user);
+ if (localuser)
+ tag = localuser->GetClass()->config;
std::string rules_name = tag->getString("rules", "rules");
ConfigFileCache::iterator rules = ServerInstance->Config->Files.find(rules_name);
if (rules == ServerInstance->Config->Files.end())
diff --git a/src/commands/cmd_stats.cpp b/src/commands/cmd_stats.cpp
index 898e89a7d..846af57db 100644
--- a/src/commands/cmd_stats.cpp
+++ b/src/commands/cmd_stats.cpp
@@ -21,7 +21,6 @@
#include "inspircd.h"
#include "xline.h"
-#include "commands/cmd_whowas.h"
#ifdef _WIN32
#include <psapi.h>
@@ -60,7 +59,7 @@ void CommandStats::DoStats(char statschar, User* user, string_list &results)
std::string sn(ServerInstance->Config->ServerName);
bool isPublic = ServerInstance->Config->UserStats.find(statschar) != std::string::npos;
- bool isRemoteOper = IS_REMOTE(user) && IS_OPER(user);
+ bool isRemoteOper = IS_REMOTE(user) && (user->IsOper());
bool isLocalOperWithPrivs = IS_LOCAL(user) && user->HasPrivPermission("servers/auspex");
if (!isPublic && !isRemoteOper && !isLocalOperWithPrivs)
@@ -167,8 +166,9 @@ void CommandStats::DoStats(char statschar, User* user, string_list &results)
User* oper = *i;
if (!ServerInstance->ULine(oper->server))
{
+ LocalUser* lu = IS_LOCAL(oper);
results.push_back(sn+" 249 " + user->nick + " :" + oper->nick + " (" + oper->ident + "@" + oper->dhost + ") Idle: " +
- (IS_LOCAL(oper) ? ConvToStr(ServerInstance->Time() - oper->idle_lastmsg) + " secs" : "unavailable"));
+ (lu ? ConvToStr(ServerInstance->Time() - lu->idle_lastmsg) + " secs" : "unavailable"));
idx++;
}
}
@@ -205,7 +205,7 @@ void CommandStats::DoStats(char statschar, User* user, string_list &results)
if (i->second->use_count)
{
/* RPL_STATSCOMMANDS */
- results.push_back(sn+" 212 "+user->nick+" "+i->second->name+" "+ConvToStr(i->second->use_count)+" "+ConvToStr(i->second->total_bytes));
+ results.push_back(sn+" 212 "+user->nick+" "+i->second->name+" "+ConvToStr(i->second->use_count));
}
}
break;
@@ -217,18 +217,6 @@ void CommandStats::DoStats(char statschar, User* user, string_list &results)
results.push_back(sn+" 249 "+user->nick+" :Channels: "+ConvToStr(ServerInstance->chanlist->size()));
results.push_back(sn+" 249 "+user->nick+" :Commands: "+ConvToStr(ServerInstance->Parser->cmdlist.size()));
- if (!ServerInstance->Config->WhoWasGroupSize == 0 && !ServerInstance->Config->WhoWasMaxGroups == 0)
- {
- Module* whowas = ServerInstance->Modules->Find("cmd_whowas.so");
- if (whowas)
- {
- WhowasRequest req(NULL, whowas, WhowasRequest::WHOWAS_STATS);
- req.user = user;
- req.Send();
- results.push_back(sn+" 249 "+user->nick+" :"+req.value);
- }
- }
-
float kbitpersec_in, kbitpersec_out, kbitpersec_total;
char kbitpersec_in_s[30], kbitpersec_out_s[30], kbitpersec_total_s[30];
@@ -283,9 +271,9 @@ void CommandStats::DoStats(char statschar, User* user, string_list &results)
}
FILETIME CreationTime;
- FILETIME ExitTime;
- FILETIME KernelTime;
- FILETIME UserTime;
+ FILETIME ExitTime;
+ FILETIME KernelTime;
+ FILETIME UserTime;
LARGE_INTEGER ThisSample;
if(GetProcessTimes(GetCurrentProcess(), &CreationTime, &ExitTime, &KernelTime, &UserTime) &&
QueryPerformanceCounter(&ThisSample))
@@ -295,7 +283,7 @@ void CommandStats::DoStats(char statschar, User* user, string_list &results)
double n_eaten = (double)( ( (uint64_t)(KernelTime.dwHighDateTime - ServerInstance->stats->LastCPU.dwHighDateTime) << 32 ) + (uint64_t)(KernelTime.dwLowDateTime - ServerInstance->stats->LastCPU.dwLowDateTime) )/100000;
double n_elapsed = (double)(ThisSample.QuadPart - ServerInstance->stats->LastSampled.QuadPart) / ServerInstance->stats->QPFrequency.QuadPart;
double per = (n_eaten/n_elapsed);
-
+
char percent[30];
snprintf(percent, 30, "%03.5f%%", per);
@@ -313,15 +301,13 @@ void CommandStats::DoStats(char statschar, User* user, string_list &results)
case 'T':
{
- char buffer[MAXBUF];
results.push_back(sn+" 249 "+user->nick+" :accepts "+ConvToStr(ServerInstance->stats->statsAccept)+" refused "+ConvToStr(ServerInstance->stats->statsRefused));
results.push_back(sn+" 249 "+user->nick+" :unknown commands "+ConvToStr(ServerInstance->stats->statsUnknown));
results.push_back(sn+" 249 "+user->nick+" :nick collisions "+ConvToStr(ServerInstance->stats->statsCollisions));
results.push_back(sn+" 249 "+user->nick+" :dns requests "+ConvToStr(ServerInstance->stats->statsDnsGood+ServerInstance->stats->statsDnsBad)+" succeeded "+ConvToStr(ServerInstance->stats->statsDnsGood)+" failed "+ConvToStr(ServerInstance->stats->statsDnsBad));
results.push_back(sn+" 249 "+user->nick+" :connection count "+ConvToStr(ServerInstance->stats->statsConnects));
- snprintf(buffer,MAXBUF," 249 %s :bytes sent %5.2fK recv %5.2fK",
- user->nick.c_str(),ServerInstance->stats->statsSent / 1024.0,ServerInstance->stats->statsRecv / 1024.0);
- results.push_back(sn+buffer);
+ results.push_back(InspIRCd::Format("%s 249 %s :bytes sent %5.2fK recv %5.2fK", sn.c_str(), user->nick.c_str(),
+ ServerInstance->stats->statsSent / 1024.0, ServerInstance->stats->statsRecv / 1024.0));
}
break;
@@ -339,11 +325,8 @@ void CommandStats::DoStats(char statschar, User* user, string_list &results)
break;
case 'O':
{
- for(OperIndex::iterator i = ServerInstance->Config->oper_blocks.begin(); i != ServerInstance->Config->oper_blocks.end(); i++)
+ for (OperIndex::const_iterator i = ServerInstance->Config->OperTypes.begin(); i != ServerInstance->Config->OperTypes.end(); ++i)
{
- // just the types, not the actual oper blocks...
- if (i->first[0] != ' ')
- continue;
OperInfo* tag = i->second;
tag->init();
std::string umodes;
@@ -357,7 +340,7 @@ void CommandStats::DoStats(char statschar, User* user, string_list &results)
if (mh && mh->NeedsOper() && tag->AllowedChanModes[c - 'A'])
cmodes.push_back(c);
}
- results.push_back(sn+" 243 "+user->nick+" O "+tag->NameStr() + " " + umodes + " " + cmodes);
+ results.push_back(sn+" 243 "+user->nick+" O "+tag->name.c_str() + " " + umodes + " " + cmodes);
}
}
break;
@@ -394,15 +377,15 @@ void CommandStats::DoStats(char statschar, User* user, string_list &results)
* Craig suggested this, and it seemed a good idea so in it went */
if (stime->tm_year > 70)
{
- char buffer[MAXBUF];
- snprintf(buffer,MAXBUF," 242 %s :Server up %d years, %d days, %.2d:%.2d:%.2d",user->nick.c_str(),(stime->tm_year-70),stime->tm_yday,stime->tm_hour,stime->tm_min,stime->tm_sec);
- results.push_back(sn+buffer);
+ results.push_back(InspIRCd::Format("%s 242 %s :Server up %d years, %d days, %.2d:%.2d:%.2d",
+ sn.c_str(), user->nick.c_str(), stime->tm_year - 70, stime->tm_yday, stime->tm_hour,
+ stime->tm_min, stime->tm_sec));
}
else
{
- char buffer[MAXBUF];
- snprintf(buffer,MAXBUF," 242 %s :Server up %d days, %.2d:%.2d:%.2d",user->nick.c_str(),stime->tm_yday,stime->tm_hour,stime->tm_min,stime->tm_sec);
- results.push_back(sn+buffer);
+ results.push_back(InspIRCd::Format("%s 242 %s :Server up %d days, %.2d:%.2d:%.2d",
+ sn.c_str(), user->nick.c_str(), stime->tm_yday, stime->tm_hour, stime->tm_min,
+ stime->tm_sec));
}
}
break;
diff --git a/src/commands/cmd_time.cpp b/src/commands/cmd_time.cpp
index db452d381..8c516ac42 100644
--- a/src/commands/cmd_time.cpp
+++ b/src/commands/cmd_time.cpp
@@ -50,16 +50,13 @@ CmdResult CommandTime::Handle (const std::vector<std::string>& parameters, User
{
if (parameters.size() > 0 && parameters[0] != ServerInstance->Config->ServerName)
return CMD_SUCCESS;
- struct tm* timeinfo;
- time_t local = ServerInstance->Time();
-
- timeinfo = localtime(&local);
- char tms[26];
- snprintf(tms,26,"%s",asctime(timeinfo));
- tms[24] = 0;
+ time_t local = ServerInstance->Time();
+ struct tm* timeinfo = localtime(&local);
+ const std::string& humanTime = asctime(timeinfo);
- user->SendText(":%s %03d %s %s :%s", ServerInstance->Config->ServerName.c_str(), RPL_TIME, user->nick.c_str(),ServerInstance->Config->ServerName.c_str(),tms);
+ user->SendText(":%s %03d %s %s :%s", ServerInstance->Config->ServerName.c_str(), RPL_TIME, user->nick.c_str(),
+ ServerInstance->Config->ServerName.c_str(), humanTime.c_str());
return CMD_SUCCESS;
}
diff --git a/src/commands/cmd_topic.cpp b/src/commands/cmd_topic.cpp
index 412ca1c06..8f5979865 100644
--- a/src/commands/cmd_topic.cpp
+++ b/src/commands/cmd_topic.cpp
@@ -27,26 +27,35 @@
* the same way, however, they can be fully unloaded, where these
* may not.
*/
-class CommandTopic : public Command
+class CommandTopic : public SplitCommand
{
+ ChanModeReference secretmode;
+ ChanModeReference topiclockmode;
+
public:
/** Constructor for topic.
*/
- CommandTopic ( Module* parent) : Command(parent,"TOPIC",1, 2) { syntax = "<channel> [<topic>]"; Penalty = 2; }
+ CommandTopic(Module* parent)
+ : SplitCommand(parent, "TOPIC", 1, 2)
+ , secretmode(parent, "secret")
+ , topiclockmode(parent, "topiclock")
+ {
+ syntax = "<channel> [<topic>]";
+ Penalty = 2;
+ }
+
/** Handle command.
* @param parameters The parameters to the comamnd
* @param pcnt The number of parameters passed to teh command
* @param user The user issuing the command
* @return A value from CmdResult to indicate command success or failure.
*/
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
+ CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser* user);
};
-CmdResult CommandTopic::Handle (const std::vector<std::string>& parameters, User *user)
+CmdResult CommandTopic::HandleLocal(const std::vector<std::string>& parameters, LocalUser* user)
{
- Channel* c;
-
- c = ServerInstance->FindChan(parameters[0]);
+ Channel* c = ServerInstance->FindChan(parameters[0]);
if (!c)
{
user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), parameters[0].c_str());
@@ -57,7 +66,7 @@ CmdResult CommandTopic::Handle (const std::vector<std::string>& parameters, User
{
if (c)
{
- if ((c->IsModeSet('s')) && (!c->HasUser(user)))
+ if ((c->IsModeSet(secretmode)) && (!c->HasUser(user)))
{
user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), c->name.c_str());
return CMD_FAILURE;
@@ -75,12 +84,28 @@ CmdResult CommandTopic::Handle (const std::vector<std::string>& parameters, User
}
return CMD_SUCCESS;
}
- else if (parameters.size()>1)
+
+ std::string t = parameters[1]; // needed, in case a module wants to change it
+ ModResult res;
+ FIRST_MOD_RESULT(OnPreTopicChange, res, (user,c,t));
+
+ if (res == MOD_RES_DENY)
+ return CMD_FAILURE;
+ if (res != MOD_RES_ALLOW)
{
- std::string t = parameters[1]; // needed, in case a module wants to change it
- c->SetTopic(user, t);
+ if (!c->HasUser(user))
+ {
+ user->WriteNumeric(442, "%s %s :You're not on that channel!", user->nick.c_str(), c->name.c_str());
+ return CMD_FAILURE;
+ }
+ if (c->IsModeSet(topiclockmode) && !ServerInstance->OnCheckExemption(user, c, "topiclock").check(c->GetPrefixValue(user) >= HALFOP_VALUE))
+ {
+ user->WriteNumeric(482, "%s %s :You do not have access to change the topic on this channel", user->nick.c_str(), c->name.c_str());
+ return CMD_FAILURE;
+ }
}
+ c->SetTopic(user, t);
return CMD_SUCCESS;
}
diff --git a/src/commands/cmd_unloadmodule.cpp b/src/commands/cmd_unloadmodule.cpp
index 6d0f5f41c..29f454987 100644
--- a/src/commands/cmd_unloadmodule.cpp
+++ b/src/commands/cmd_unloadmodule.cpp
@@ -42,12 +42,19 @@ class CommandUnloadmodule : public Command
CmdResult CommandUnloadmodule::Handle (const std::vector<std::string>& parameters, User *user)
{
+ if (!ServerInstance->Config->ConfValue("security")->getBool("allowcoreunload") &&
+ InspIRCd::Match(parameters[0], "cmd_*.so", ascii_case_insensitive_map))
+ {
+ user->WriteNumeric(972, "%s %s :You cannot unload core commands!", user->nick.c_str(), parameters[0].c_str());
+ return CMD_FAILURE;
+ }
+
if (parameters[0] == "cmd_unloadmodule.so" || parameters[0] == "cmd_loadmodule.so")
{
user->WriteNumeric(972, "%s %s :You cannot unload module loading commands!", user->nick.c_str(), parameters[0].c_str());
return CMD_FAILURE;
}
-
+
Module* m = ServerInstance->Modules->Find(parameters[0]);
if (m && ServerInstance->Modules->Unload(m))
{
diff --git a/src/commands/cmd_user.cpp b/src/commands/cmd_user.cpp
index 305d0847f..b4d86f07b 100644
--- a/src/commands/cmd_user.cpp
+++ b/src/commands/cmd_user.cpp
@@ -45,7 +45,7 @@ CmdResult CommandUser::HandleLocal(const std::vector<std::string>& parameters, L
/* A user may only send the USER command once */
if (!(user->registered & REG_USER))
{
- if (!ServerInstance->IsIdent(parameters[0].c_str()))
+ if (!ServerInstance->IsIdent(parameters[0]))
{
/*
* RFC says we must use this numeric, so we do. Let's make it a little more nub friendly though. :)
@@ -61,7 +61,7 @@ CmdResult CommandUser::HandleLocal(const std::vector<std::string>& parameters, L
* ~ character, and +1 for null termination, therefore we can safely use up to
* IDENTMAX here.
*/
- user->ChangeIdent(parameters[0].c_str());
+ user->ChangeIdent(parameters[0]);
user->fullname.assign(parameters[3].empty() ? "No info" : parameters[3], 0, ServerInstance->Config->Limits.MaxGecos);
user->registered = (user->registered | REG_USER);
}
diff --git a/src/commands/cmd_userhost.cpp b/src/commands/cmd_userhost.cpp
index 399de0b1a..933cbca04 100644
--- a/src/commands/cmd_userhost.cpp
+++ b/src/commands/cmd_userhost.cpp
@@ -54,12 +54,12 @@ CmdResult CommandUserhost::Handle (const std::vector<std::string>& parameters, U
{
retbuf = retbuf + u->nick;
- if (IS_OPER(u))
+ if (u->IsOper())
retbuf = retbuf + "*";
retbuf = retbuf + "=";
- if (IS_AWAY(u))
+ if (u->IsAway())
retbuf += "-";
else
retbuf += "+";
diff --git a/src/commands/cmd_version.cpp b/src/commands/cmd_version.cpp
index 7620197fd..9fdd9c838 100644
--- a/src/commands/cmd_version.cpp
+++ b/src/commands/cmd_version.cpp
@@ -42,9 +42,13 @@ class CommandVersion : public Command
CmdResult CommandVersion::Handle (const std::vector<std::string>&, User *user)
{
- std::string version = ServerInstance->GetVersionString(IS_OPER(user));
+ std::string version = ServerInstance->GetVersionString((user->IsOper()));
user->WriteNumeric(RPL_VERSION, "%s :%s", user->nick.c_str(), version.c_str());
- ServerInstance->Config->Send005(user);
+ LocalUser *lu = IS_LOCAL(user);
+ if (lu != NULL)
+ {
+ ServerInstance->ISupport.SendTo(lu);
+ }
return CMD_SUCCESS;
}
diff --git a/src/commands/cmd_wallops.cpp b/src/commands/cmd_wallops.cpp
index 198997a95..e0e832ff7 100644
--- a/src/commands/cmd_wallops.cpp
+++ b/src/commands/cmd_wallops.cpp
@@ -27,10 +27,19 @@
*/
class CommandWallops : public Command
{
+ UserModeReference wallopsmode;
+
public:
/** Constructor for wallops.
*/
- CommandWallops ( Module* parent) : Command(parent,"WALLOPS",1,1) { flags_needed = 'o'; syntax = "<any-text>"; }
+ CommandWallops(Module* parent)
+ : Command(parent, "WALLOPS", 1, 1)
+ , wallopsmode(parent, "wallops")
+ {
+ flags_needed = 'o';
+ syntax = "<any-text>";
+ }
+
/** Handle command.
* @param parameters The parameters to the comamnd
* @param pcnt The number of parameters passed to teh command
@@ -38,6 +47,11 @@ class CommandWallops : public Command
* @return A value from CmdResult to indicate command success or failure.
*/
CmdResult Handle(const std::vector<std::string>& parameters, User *user);
+
+ RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+ {
+ return ROUTE_BROADCAST;
+ }
};
CmdResult CommandWallops::Handle (const std::vector<std::string>& parameters, User *user)
@@ -48,11 +62,10 @@ CmdResult CommandWallops::Handle (const std::vector<std::string>& parameters, Us
for (LocalUserList::const_iterator i = ServerInstance->Users->local_users.begin(); i != ServerInstance->Users->local_users.end(); i++)
{
User* t = *i;
- if (t->IsModeSet('w'))
+ if (t->IsModeSet(wallopsmode))
user->WriteTo(t,wallop);
}
- FOREACH_MOD(I_OnWallops,OnWallops(user,parameters[0]));
return CMD_SUCCESS;
}
diff --git a/src/commands/cmd_who.cpp b/src/commands/cmd_who.cpp
index f8926b9f7..5e0e55b66 100644
--- a/src/commands/cmd_who.cpp
+++ b/src/commands/cmd_who.cpp
@@ -39,13 +39,34 @@ class CommandWho : public Command
bool opt_local;
bool opt_far;
bool opt_time;
+ ChanModeReference secretmode;
+ ChanModeReference privatemode;
+ UserModeReference invisiblemode;
+
+ Channel* get_first_visible_channel(User *u)
+ {
+ UCListIter i = u->chans.begin();
+ while (i != u->chans.end())
+ {
+ Channel* c = *i++;
+ if (!c->IsModeSet(secretmode))
+ return c;
+ }
+ return NULL;
+ }
public:
/** Constructor for who.
*/
- CommandWho ( Module* parent) : Command(parent,"WHO", 1) {
+ CommandWho(Module* parent)
+ : Command(parent, "WHO", 1)
+ , secretmode(parent, "secret")
+ , privatemode(parent, "private")
+ , invisiblemode(parent, "invisible")
+ {
syntax = "<server>|<nickname>|<channel>|<realname>|<host>|0 [ohurmMiaplf]";
}
+
void SendWhoLine(User* user, const std::vector<std::string>& parms, const std::string &initial, Channel* ch, User* u, std::vector<std::string> &whoresults);
/** Handle command.
* @param parameters The parameters to the comamnd
@@ -57,19 +78,6 @@ class CommandWho : public Command
bool whomatch(User* cuser, User* user, const char* matchtext);
};
-
-static Channel* get_first_visible_channel(User *u)
-{
- UCListIter i = u->chans.begin();
- while (i != u->chans.end())
- {
- Channel* c = *i++;
- if (!c->IsModeSet('s'))
- return c;
- }
- return NULL;
-}
-
bool CommandWho::whomatch(User* cuser, User* user, const char* matchtext)
{
bool match = false;
@@ -138,7 +146,7 @@ bool CommandWho::whomatch(User* cuser, User* user, const char* matchtext)
match = InspIRCd::Match(user->awaymsg, matchtext);
else if (opt_time)
{
- long seconds = ServerInstance->Duration(matchtext);
+ long seconds = InspIRCd::Duration(matchtext);
// Okay, so time matching, we want all users connected `seconds' ago
if (user->age >= ServerInstance->Time() - seconds)
@@ -180,7 +188,7 @@ bool CommandWho::CanView(Channel* chan, User* user)
if (user->HasPrivPermission("users/auspex"))
return true;
/* Cant see inside a +s or a +p channel unless we are a member (see above) */
- else if (!chan->IsModeSet('s') && !chan->IsModeSet('p'))
+ else if (!chan->IsModeSet(secretmode) && !chan->IsModeSet(privatemode))
return true;
return false;
@@ -197,11 +205,11 @@ void CommandWho::SendWhoLine(User* user, const std::vector<std::string>& parms,
wholine.append(ServerInstance->Config->HideWhoisServer);
else
wholine.append(u->server);
-
+
wholine.append(" " + u->nick + " ");
/* away? */
- if (IS_AWAY(u))
+ if (u->IsAway())
{
wholine.append("G");
}
@@ -211,7 +219,7 @@ void CommandWho::SendWhoLine(User* user, const std::vector<std::string>& parms,
}
/* oper? */
- if (IS_OPER(u))
+ if (u->IsOper())
{
wholine.push_back('*');
}
@@ -221,7 +229,7 @@ void CommandWho::SendWhoLine(User* user, const std::vector<std::string>& parms,
wholine.append(" :0 " + u->fullname);
- FOREACH_MOD(I_OnSendWhoLine, OnSendWhoLine(user, parms, u, wholine));
+ FOREACH_MOD(OnSendWhoLine, (user, parms, u, wholine));
if (!wholine.empty())
whoresults.push_back(wholine);
@@ -249,33 +257,17 @@ CmdResult CommandWho::Handle (const std::vector<std::string>& parameters, User *
opt_far = false;
opt_time = false;
- Channel *ch = NULL;
std::vector<std::string> whoresults;
std::string initial = "352 " + user->nick + " ";
- char matchtext[MAXBUF];
- bool usingwildcards = false;
-
/* Change '0' into '*' so the wildcard matcher can grok it */
- if (parameters[0] == "0")
- strlcpy(matchtext, "*", MAXBUF);
- else
- strlcpy(matchtext, parameters[0].c_str(), MAXBUF);
+ std::string matchtext = ((parameters[0] == "0") ? "*" : parameters[0]);
- for (const char* check = matchtext; *check; check++)
- {
- if (*check == '*' || *check == '?' || *check == '.')
- {
- usingwildcards = true;
- break;
- }
- }
+ // WHO flags count as a wildcard
+ bool usingwildcards = ((parameters.size() > 1) || (matchtext.find_first_of("*?.") != std::string::npos));
if (parameters.size() > 1)
{
- /* Fix for bug #444, WHO flags count as a wildcard */
- usingwildcards = true;
-
for (std::string::const_iterator iter = parameters[1].begin(); iter != parameters[1].end(); ++iter)
{
switch (*iter)
@@ -325,7 +317,7 @@ CmdResult CommandWho::Handle (const std::vector<std::string>& parameters, User *
/* who on a channel? */
- ch = ServerInstance->FindChan(matchtext);
+ Channel* ch = ServerInstance->FindChan(matchtext);
if (ch)
{
@@ -342,11 +334,11 @@ CmdResult CommandWho::Handle (const std::vector<std::string>& parameters, User *
if (user != i->first)
{
/* opers only, please */
- if (opt_viewopersonly && !IS_OPER(i->first))
+ if (opt_viewopersonly && !i->first->IsOper())
continue;
/* If we're not inside the channel, hide +i users */
- if (i->first->IsModeSet('i') && !inside && !user->HasPrivPermission("users/auspex"))
+ if (i->first->IsModeSet(invisiblemode) && !inside && !user->HasPrivPermission("users/auspex"))
continue;
}
@@ -364,11 +356,11 @@ CmdResult CommandWho::Handle (const std::vector<std::string>& parameters, User *
{
User* oper = *i;
- if (whomatch(user, oper, matchtext))
+ if (whomatch(user, oper, matchtext.c_str()))
{
if (!user->SharesChannelWith(oper))
{
- if (usingwildcards && (!oper->IsModeSet('i')) && (!user->HasPrivPermission("users/auspex")))
+ if (usingwildcards && (!oper->IsModeSet(invisiblemode)) && (!user->HasPrivPermission("users/auspex")))
continue;
}
@@ -380,11 +372,11 @@ CmdResult CommandWho::Handle (const std::vector<std::string>& parameters, User *
{
for (user_hash::iterator i = ServerInstance->Users->clientlist->begin(); i != ServerInstance->Users->clientlist->end(); i++)
{
- if (whomatch(user, i->second, matchtext))
+ if (whomatch(user, i->second, matchtext.c_str()))
{
if (!user->SharesChannelWith(i->second))
{
- if (usingwildcards && (i->second->IsModeSet('i')) && (!user->HasPrivPermission("users/auspex")))
+ if (usingwildcards && (i->second->IsModeSet(invisiblemode)) && (!user->HasPrivPermission("users/auspex")))
continue;
}
diff --git a/src/commands/cmd_whois.cpp b/src/commands/cmd_whois.cpp
index ba2ad9c15..0df6b65f0 100644
--- a/src/commands/cmd_whois.cpp
+++ b/src/commands/cmd_whois.cpp
@@ -26,32 +26,191 @@
* the same way, however, they can be fully unloaded, where these
* may not.
*/
-class CommandWhois : public Command
+class CommandWhois : public SplitCommand
{
+ ChanModeReference secretmode;
+ ChanModeReference privatemode;
+ UserModeReference snomaskmode;
+
+ void SplitChanList(User* source, User* dest, const std::string& cl);
+ void DoWhois(User* user, User* dest, unsigned long signon, unsigned long idle);
+ std::string ChannelList(User* source, User* dest, bool spy);
+
public:
/** Constructor for whois.
*/
- CommandWhois ( Module* parent) : Command(parent,"WHOIS",1) { Penalty = 2; syntax = "<nick>{,<nick>}"; }
+ CommandWhois(Module* parent)
+ : SplitCommand(parent, "WHOIS", 1)
+ , secretmode(parent, "secret")
+ , privatemode(parent, "private")
+ , snomaskmode(parent, "snomask")
+ {
+ Penalty = 2;
+ syntax = "<nick>{,<nick>}";
+ }
+
/** Handle command.
* @param parameters The parameters to the comamnd
* @param pcnt The number of parameters passed to teh command
* @param user The user issuing the command
* @return A value from CmdResult to indicate command success or failure.
*/
- CmdResult Handle(const std::vector<std::string>& parameters, User *user);
+ CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser* user);
+ CmdResult HandleRemote(const std::vector<std::string>& parameters, RemoteUser* target);
};
+std::string CommandWhois::ChannelList(User* source, User* dest, bool spy)
+{
+ std::string list;
-CmdResult CommandWhois::Handle (const std::vector<std::string>& parameters, User *user)
+ for (UCListIter i = dest->chans.begin(); i != dest->chans.end(); i++)
+ {
+ Channel* c = *i;
+ /* If the target is the sender, neither +p nor +s is set, or
+ * the channel contains the user, it is not a spy channel
+ */
+ if (spy != (source == dest || !(c->IsModeSet(privatemode) || c->IsModeSet(secretmode)) || c->HasUser(source)))
+ list.append(c->GetPrefixChar(dest)).append(c->name).append(" ");
+ }
+
+ return list;
+}
+
+void CommandWhois::SplitChanList(User* source, User* dest, const std::string& cl)
+{
+ std::string line;
+ std::ostringstream prefix;
+ std::string::size_type start, pos, length;
+
+ prefix << source->nick << " " << dest->nick << " :";
+ line = prefix.str();
+ int namelen = ServerInstance->Config->ServerName.length() + 6;
+
+ for (start = 0; (pos = cl.find(' ', start)) != std::string::npos; start = pos+1)
+ {
+ length = (pos == std::string::npos) ? cl.length() : pos;
+
+ if (line.length() + namelen + length - start > 510)
+ {
+ ServerInstance->SendWhoisLine(source, dest, 319, "%s", line.c_str());
+ line = prefix.str();
+ }
+
+ if(pos == std::string::npos)
+ {
+ line.append(cl.substr(start, length - start));
+ break;
+ }
+ else
+ {
+ line.append(cl.substr(start, length - start + 1));
+ }
+ }
+
+ if (line.length() != prefix.str().length())
+ {
+ ServerInstance->SendWhoisLine(source, dest, 319, "%s", line.c_str());
+ }
+}
+
+void CommandWhois::DoWhois(User* user, User* dest, unsigned long signon, unsigned long idle)
+{
+ ServerInstance->SendWhoisLine(user, dest, 311, "%s %s %s %s * :%s",user->nick.c_str(), dest->nick.c_str(), dest->ident.c_str(), dest->dhost.c_str(), dest->fullname.c_str());
+ if (user == dest || user->HasPrivPermission("users/auspex"))
+ {
+ ServerInstance->SendWhoisLine(user, dest, 378, "%s %s :is connecting from %s@%s %s", user->nick.c_str(), dest->nick.c_str(), dest->ident.c_str(), dest->host.c_str(), dest->GetIPString().c_str());
+ }
+
+ std::string cl = ChannelList(user, dest, false);
+ const ServerConfig::OperSpyWhoisState state = user->HasPrivPermission("users/auspex") ? ServerInstance->Config->OperSpyWhois : ServerConfig::SPYWHOIS_NONE;
+
+ if (state == ServerConfig::SPYWHOIS_SINGLEMSG)
+ cl.append(ChannelList(user, dest, true));
+
+ SplitChanList(user, dest, cl);
+
+ if (state == ServerConfig::SPYWHOIS_SPLITMSG)
+ {
+ std::string scl = ChannelList(user, dest, true);
+ if (scl.length())
+ {
+ ServerInstance->SendWhoisLine(user, dest, 336, "%s %s :is on private/secret channels:",user->nick.c_str(), dest->nick.c_str());
+ SplitChanList(user, dest, scl);
+ }
+ }
+ if (user != dest && !ServerInstance->Config->HideWhoisServer.empty() && !user->HasPrivPermission("servers/auspex"))
+ {
+ ServerInstance->SendWhoisLine(user, dest, 312, "%s %s %s :%s",user->nick.c_str(), dest->nick.c_str(), ServerInstance->Config->HideWhoisServer.c_str(), ServerInstance->Config->Network.c_str());
+ }
+ else
+ {
+ std::string serverdesc = ServerInstance->GetServerDescription(dest->server);
+ ServerInstance->SendWhoisLine(user, dest, 312, "%s %s %s :%s",user->nick.c_str(), dest->nick.c_str(), dest->server.c_str(), serverdesc.c_str());
+ }
+
+ if (dest->IsAway())
+ {
+ ServerInstance->SendWhoisLine(user, dest, 301, "%s %s :%s",user->nick.c_str(), dest->nick.c_str(), dest->awaymsg.c_str());
+ }
+
+ if (dest->IsOper())
+ {
+ if (ServerInstance->Config->GenericOper)
+ ServerInstance->SendWhoisLine(user, dest, 313, "%s %s :is an IRC operator",user->nick.c_str(), dest->nick.c_str());
+ else
+ ServerInstance->SendWhoisLine(user, dest, 313, "%s %s :is %s %s on %s",user->nick.c_str(), dest->nick.c_str(), (strchr("AEIOUaeiou",dest->oper->name[0]) ? "an" : "a"),dest->oper->name.c_str(), ServerInstance->Config->Network.c_str());
+ }
+
+ if (user == dest || user->HasPrivPermission("users/auspex"))
+ {
+ if (dest->IsModeSet(snomaskmode))
+ {
+ ServerInstance->SendWhoisLine(user, dest, 379, "%s %s :is using modes +%s %s", user->nick.c_str(), dest->nick.c_str(), dest->FormatModes(), snomaskmode->GetUserParameter(dest).c_str());
+ }
+ else
+ {
+ ServerInstance->SendWhoisLine(user, dest, 379, "%s %s :is using modes +%s", user->nick.c_str(), dest->nick.c_str(), dest->FormatModes());
+ }
+ }
+
+ FOREACH_MOD(OnWhois, (user,dest));
+
+ /*
+ * We only send these if we've been provided them. That is, if hidewhois is turned off, and user is local, or
+ * if remote whois is queried, too. This is to keep the user hidden, and also since you can't reliably tell remote time. -- w00t
+ */
+ if ((idle) || (signon))
+ {
+ ServerInstance->SendWhoisLine(user, dest, 317, "%s %s %lu %lu :seconds idle, signon time",user->nick.c_str(), dest->nick.c_str(), idle, signon);
+ }
+
+ ServerInstance->SendWhoisLine(user, dest, 318, "%s %s :End of /WHOIS list.",user->nick.c_str(), dest->nick.c_str());
+}
+
+CmdResult CommandWhois::HandleRemote(const std::vector<std::string>& parameters, RemoteUser* target)
+{
+ if (parameters.size() < 2)
+ return CMD_FAILURE;
+
+ User* user = ServerInstance->FindUUID(parameters[0]);
+ if (!user)
+ return CMD_FAILURE;
+
+ unsigned long idle = ConvToInt(parameters.back());
+ DoWhois(user, target, target->signon, idle);
+
+ return CMD_SUCCESS;
+}
+
+CmdResult CommandWhois::HandleLocal(const std::vector<std::string>& parameters, LocalUser* user)
{
User *dest;
int userindex = 0;
unsigned long idle = 0, signon = 0;
- if (ServerInstance->Parser->LoopCall(user, this, parameters, 0))
+ if (CommandParser::LoopCall(user, this, parameters, 0))
return CMD_SUCCESS;
-
/*
* If 2 paramters are specified (/whois nick nick), ignore the first one like spanningtree
* does, and use the second one, otherwise, use the only paramter. -- djGrrr
@@ -59,10 +218,7 @@ CmdResult CommandWhois::Handle (const std::vector<std::string>& parameters, User
if (parameters.size() > 1)
userindex = 1;
- if (IS_LOCAL(user))
- dest = ServerInstance->FindNickOnly(parameters[userindex]);
- else
- dest = ServerInstance->FindNick(parameters[userindex]);
+ dest = ServerInstance->FindNickOnly(parameters[userindex]);
if ((dest) && (dest->registered == REG_ALL))
{
@@ -74,13 +230,14 @@ CmdResult CommandWhois::Handle (const std::vector<std::string>& parameters, User
* For remote users (/w remoteuser remoteuser), spanningtree will handle calling do_whois, so we can ignore this case.
* Thanks to djGrrr for not being impatient while I have a crap day coding. :p -- w00t
*/
- if (IS_LOCAL(dest) && (ServerInstance->Config->HideWhoisServer.empty() || parameters.size() > 1))
+ LocalUser* localuser = IS_LOCAL(dest);
+ if (localuser && (ServerInstance->Config->HideWhoisServer.empty() || parameters.size() > 1))
{
- idle = abs((long)((dest->idle_lastmsg)-ServerInstance->Time()));
+ idle = abs((long)((localuser->idle_lastmsg)-ServerInstance->Time()));
signon = dest->signon;
}
- ServerInstance->DoWhois(user,dest,signon,idle,parameters[userindex].c_str());
+ DoWhois(user,dest,signon,idle);
}
else
{
@@ -93,6 +250,4 @@ CmdResult CommandWhois::Handle (const std::vector<std::string>& parameters, User
return CMD_SUCCESS;
}
-
-
COMMAND_INIT(CommandWhois)
diff --git a/src/commands/cmd_whowas.cpp b/src/commands/cmd_whowas.cpp
index 3a6444b45..75d1ee8ff 100644
--- a/src/commands/cmd_whowas.cpp
+++ b/src/commands/cmd_whowas.cpp
@@ -23,20 +23,18 @@
#include "inspircd.h"
#include "commands/cmd_whowas.h"
-WhoWasMaintainTimer * timer;
-
-CommandWhowas::CommandWhowas( Module* parent) : Command(parent, "WHOWAS", 1)
+CommandWhowas::CommandWhowas( Module* parent)
+ : Command(parent, "WHOWAS", 1)
+ , GroupSize(0), MaxGroups(0), MaxKeep(0)
{
syntax = "<nick>{,<nick>}";
Penalty = 2;
- timer = new WhoWasMaintainTimer(3600);
- ServerInstance->Timers->AddTimer(timer);
}
CmdResult CommandWhowas::Handle (const std::vector<std::string>& parameters, User* user)
{
/* if whowas disabled in config */
- if (ServerInstance->Config->WhoWasGroupSize == 0 || ServerInstance->Config->WhoWasMaxGroups == 0)
+ if (this->GroupSize == 0 || this->MaxGroups == 0)
{
user->WriteNumeric(421, "%s %s :This command has been disabled.",user->nick.c_str(),name.c_str());
return CMD_FAILURE;
@@ -47,13 +45,11 @@ CmdResult CommandWhowas::Handle (const std::vector<std::string>& parameters, Use
if (i == whowas.end())
{
user->WriteNumeric(406, "%s %s :There was no such nickname",user->nick.c_str(),parameters[0].c_str());
- user->WriteNumeric(369, "%s %s :End of WHOWAS",user->nick.c_str(),parameters[0].c_str());
- return CMD_FAILURE;
}
else
{
whowas_set* grp = i->second;
- if (grp->size())
+ if (!grp->empty())
{
for (whowas_set::iterator ux = grp->begin(); ux != grp->end(); ux++)
{
@@ -67,17 +63,13 @@ CmdResult CommandWhowas::Handle (const std::vector<std::string>& parameters, Use
user->nick.c_str(), parameters[0].c_str(), u->host.c_str());
std::string signon = ServerInstance->TimeString(u->signon);
- if (!ServerInstance->Config->HideWhoisServer.empty() && !user->HasPrivPermission("servers/auspex"))
- user->WriteNumeric(312, "%s %s %s :%s",user->nick.c_str(),parameters[0].c_str(), ServerInstance->Config->HideWhoisServer.c_str(), signon.c_str());
- else
- user->WriteNumeric(312, "%s %s %s :%s",user->nick.c_str(),parameters[0].c_str(), u->server.c_str(), signon.c_str());
+ bool hide_server = (!ServerInstance->Config->HideWhoisServer.empty() && !user->HasPrivPermission("servers/auspex"));
+ user->WriteNumeric(312, "%s %s %s :%s",user->nick.c_str(), parameters[0].c_str(), (hide_server ? ServerInstance->Config->HideWhoisServer.c_str() : u->server.c_str()), signon.c_str());
}
}
else
{
user->WriteNumeric(406, "%s %s :There was no such nickname",user->nick.c_str(),parameters[0].c_str());
- user->WriteNumeric(369, "%s %s :End of WHOWAS",user->nick.c_str(),parameters[0].c_str());
- return CMD_FAILURE;
}
}
@@ -89,15 +81,11 @@ std::string CommandWhowas::GetStats()
{
int whowas_size = 0;
int whowas_bytes = 0;
- whowas_users_fifo::iterator iter;
- for (iter = whowas_fifo.begin(); iter != whowas_fifo.end(); iter++)
+ for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); ++i)
{
- whowas_set* n = (whowas_set*)whowas.find(iter->second)->second;
- if (n->size())
- {
- whowas_size += n->size();
- whowas_bytes += (sizeof(whowas_set) + ( sizeof(WhoWasGroup) * n->size() ) );
- }
+ whowas_set* n = i->second;
+ whowas_size += n->size();
+ whowas_bytes += (sizeof(whowas_set) + ( sizeof(WhoWasGroup) * n->size() ) );
}
return "Whowas entries: " +ConvToStr(whowas_size)+" ("+ConvToStr(whowas_bytes)+" bytes)";
}
@@ -105,97 +93,81 @@ std::string CommandWhowas::GetStats()
void CommandWhowas::AddToWhoWas(User* user)
{
/* if whowas disabled */
- if (ServerInstance->Config->WhoWasGroupSize == 0 || ServerInstance->Config->WhoWasMaxGroups == 0)
+ if (this->GroupSize == 0 || this->MaxGroups == 0)
{
return;
}
- whowas_users::iterator iter = whowas.find(irc::string(user->nick.c_str()));
+ // Insert nick if it doesn't exist
+ // 'first' will point to the newly inserted element or to the existing element with an equivalent key
+ std::pair<whowas_users::iterator, bool> ret = whowas.insert(std::make_pair(irc::string(user->nick.c_str()), static_cast<whowas_set*>(NULL)));
- if (iter == whowas.end())
+ if (ret.second) // If inserted
{
+ // This nick is new, create a list for it and add the first record to it
whowas_set* n = new whowas_set;
- WhoWasGroup *a = new WhoWasGroup(user);
- n->push_back(a);
- whowas[user->nick.c_str()] = n;
- whowas_fifo.push_back(std::make_pair(ServerInstance->Time(),user->nick.c_str()));
+ n->push_back(new WhoWasGroup(user));
+ ret.first->second = n;
- if ((int)(whowas.size()) > ServerInstance->Config->WhoWasMaxGroups)
+ // Add this nick to the fifo too
+ whowas_fifo.push_back(std::make_pair(ServerInstance->Time(), ret.first->first));
+
+ if (whowas.size() > this->MaxGroups)
{
- whowas_users::iterator iter2 = whowas.find(whowas_fifo[0].second);
- if (iter2 != whowas.end())
+ // Too many nicks, remove the nick which was inserted the longest time ago from both the map and the fifo
+ whowas_users::iterator it = whowas.find(whowas_fifo.front().second);
+ if (it != whowas.end())
{
- whowas_set* n2 = (whowas_set*)iter2->second;
-
- if (n2->size())
- {
- while (n2->begin() != n2->end())
- {
- WhoWasGroup *a2 = *(n2->begin());
- delete a2;
- n2->pop_front();
- }
- }
-
- delete n2;
- whowas.erase(iter2);
+ whowas_set* set = it->second;
+ for (whowas_set::iterator i = set->begin(); i != set->end(); ++i)
+ delete *i;
+
+ delete set;
+ whowas.erase(it);
}
whowas_fifo.pop_front();
}
}
else
{
- whowas_set* group = (whowas_set*)iter->second;
- WhoWasGroup *a = new WhoWasGroup(user);
- group->push_back(a);
+ // We've met this nick before, add a new record to the list
+ whowas_set* set = ret.first->second;
+ set->push_back(new WhoWasGroup(user));
- if ((int)(group->size()) > ServerInstance->Config->WhoWasGroupSize)
+ // If there are too many records for this nick, remove the oldest (front)
+ if (set->size() > this->GroupSize)
{
- WhoWasGroup *a2 = (WhoWasGroup*)*(group->begin());
- delete a2;
- group->pop_front();
+ delete set->front();
+ set->pop_front();
}
}
}
/* on rehash, refactor maps according to new conf values */
-void CommandWhowas::PruneWhoWas(time_t t)
+void CommandWhowas::Prune()
{
- /* config values */
- int groupsize = ServerInstance->Config->WhoWasGroupSize;
- int maxgroups = ServerInstance->Config->WhoWasMaxGroups;
- int maxkeep = ServerInstance->Config->WhoWasMaxKeep;
+ time_t min = ServerInstance->Time() - this->MaxKeep;
/* first cut the list to new size (maxgroups) and also prune entries that are timed out. */
- whowas_users::iterator iter;
- int fifosize;
- while ((fifosize = (int)whowas_fifo.size()) > 0)
+ while (!whowas_fifo.empty())
{
- if (fifosize > maxgroups || whowas_fifo[0].first < t - maxkeep)
+ if ((whowas_fifo.size() > this->MaxGroups) || (whowas_fifo.front().first < min))
{
- iter = whowas.find(whowas_fifo[0].second);
+ whowas_users::iterator iter = whowas.find(whowas_fifo.front().second);
/* hopefully redundant integrity check, but added while debugging r6216 */
if (iter == whowas.end())
{
/* this should never happen, if it does maps are corrupt */
- ServerInstance->Logs->Log("WHOWAS",DEFAULT, "BUG: Whowas maps got corrupted! (1)");
+ ServerInstance->Logs->Log("WHOWAS", LOG_DEFAULT, "BUG: Whowas maps got corrupted! (1)");
return;
}
- whowas_set* n = (whowas_set*)iter->second;
-
- if (n->size())
- {
- while (n->begin() != n->end())
- {
- WhoWasGroup *a = *(n->begin());
- delete a;
- n->pop_front();
- }
- }
+ whowas_set* set = iter->second;
+ for (whowas_set::iterator i = set->begin(); i != set->end(); ++i)
+ delete *i;
- delete n;
+ delete set;
whowas.erase(iter);
whowas_fifo.pop_front();
}
@@ -204,86 +176,41 @@ void CommandWhowas::PruneWhoWas(time_t t)
}
/* Then cut the whowas sets to new size (groupsize) */
- fifosize = (int)whowas_fifo.size();
- for (int i = 0; i < fifosize; i++)
+ for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); ++i)
{
- iter = whowas.find(whowas_fifo[0].second);
- /* hopefully redundant integrity check, but added while debugging r6216 */
- if (iter == whowas.end())
+ whowas_set* n = i->second;
+ while (n->size() > this->GroupSize)
{
- /* this should never happen, if it does maps are corrupt */
- ServerInstance->Logs->Log("WHOWAS",DEFAULT, "BUG: Whowas maps got corrupted! (2)");
- return;
- }
- whowas_set* n = (whowas_set*)iter->second;
- if (n->size())
- {
- int nickcount = n->size();
- while (n->begin() != n->end() && nickcount > groupsize)
- {
- WhoWasGroup *a = *(n->begin());
- delete a;
- n->pop_front();
- nickcount--;
- }
+ delete n->front();
+ n->pop_front();
}
}
}
/* call maintain once an hour to remove expired nicks */
-void CommandWhowas::MaintainWhoWas(time_t t)
+void CommandWhowas::Maintain()
{
- for (whowas_users::iterator iter = whowas.begin(); iter != whowas.end(); iter++)
+ time_t min = ServerInstance->Time() - this->MaxKeep;
+ for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); ++i)
{
- whowas_set* n = (whowas_set*)iter->second;
- if (n->size())
+ whowas_set* set = i->second;
+ while (!set->empty() && set->front()->signon < min)
{
- while ((n->begin() != n->end()) && ((*n->begin())->signon < t - ServerInstance->Config->WhoWasMaxKeep))
- {
- WhoWasGroup *a = *(n->begin());
- delete a;
- n->erase(n->begin());
- }
+ delete set->front();
+ set->pop_front();
}
}
}
CommandWhowas::~CommandWhowas()
{
- if (timer)
+ for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); ++i)
{
- ServerInstance->Timers->DelTimer(timer);
- }
+ whowas_set* set = i->second;
+ for (whowas_set::iterator j = set->begin(); j != set->end(); ++j)
+ delete *j;
- whowas_users::iterator iter;
- int fifosize;
- while ((fifosize = (int)whowas_fifo.size()) > 0)
- {
- iter = whowas.find(whowas_fifo[0].second);
-
- /* hopefully redundant integrity check, but added while debugging r6216 */
- if (iter == whowas.end())
- {
- /* this should never happen, if it does maps are corrupt */
- ServerInstance->Logs->Log("WHOWAS",DEFAULT, "BUG: Whowas maps got corrupted! (3)");
- return;
- }
-
- whowas_set* n = (whowas_set*)iter->second;
-
- if (n->size())
- {
- while (n->begin() != n->end())
- {
- WhoWasGroup *a = *(n->begin());
- delete a;
- n->pop_front();
- }
- }
-
- delete n;
- whowas.erase(iter);
- whowas_fifo.pop_front();
+ delete set;
}
}
@@ -292,23 +219,10 @@ WhoWasGroup::WhoWasGroup(User* user) : host(user->host), dhost(user->dhost), ide
{
}
-WhoWasGroup::~WhoWasGroup()
-{
-}
-
-/* every hour, run this function which removes all entries older than Config->WhoWasMaxKeep */
-void WhoWasMaintainTimer::Tick(time_t)
-{
- Module* whowas = ServerInstance->Modules->Find("cmd_whowas.so");
- if (whowas)
- {
- WhowasRequest(whowas, whowas, WhowasRequest::WHOWAS_MAINTAIN).Send();
- }
-}
-
class ModuleWhoWas : public Module
{
CommandWhowas cmd;
+
public:
ModuleWhoWas() : cmd(this)
{
@@ -317,31 +231,47 @@ class ModuleWhoWas : public Module
void init()
{
ServerInstance->Modules->AddService(cmd);
+ OnRehash(NULL);
}
- void OnRequest(Request& request)
+ void OnGarbageCollect()
{
- WhowasRequest& req = static_cast<WhowasRequest&>(request);
- switch (req.type)
- {
- case WhowasRequest::WHOWAS_ADD:
- cmd.AddToWhoWas(req.user);
- break;
- case WhowasRequest::WHOWAS_STATS:
- req.value = cmd.GetStats();
- break;
- case WhowasRequest::WHOWAS_PRUNE:
- cmd.PruneWhoWas(ServerInstance->Time());
- break;
- case WhowasRequest::WHOWAS_MAINTAIN:
- cmd.MaintainWhoWas(ServerInstance->Time());
- break;
- }
+ // Remove all entries older than MaxKeep
+ cmd.Maintain();
+ }
+
+ void OnUserQuit(User* user, const std::string& message, const std::string& oper_message)
+ {
+ cmd.AddToWhoWas(user);
+ }
+
+ ModResult OnStats(char symbol, User* user, string_list &results)
+ {
+ if (symbol == 'z')
+ results.push_back(ServerInstance->Config->ServerName+" 249 "+user->nick+" :"+cmd.GetStats());
+
+ return MOD_RES_PASSTHRU;
+ }
+
+ void OnRehash(User* user)
+ {
+ ConfigTag* tag = ServerInstance->Config->ConfValue("whowas");
+ unsigned int NewGroupSize = tag->getInt("groupsize", 10, 0, 10000);
+ unsigned int NewMaxGroups = tag->getInt("maxgroups", 10240, 0, 1000000);
+ unsigned int NewMaxKeep = tag->getDuration("maxkeep", 3600, 3600);
+
+ if ((NewGroupSize == cmd.GroupSize) && (NewMaxGroups == cmd.MaxGroups) && (NewMaxKeep == cmd.MaxKeep))
+ return;
+
+ cmd.GroupSize = NewGroupSize;
+ cmd.MaxGroups = NewMaxGroups;
+ cmd.MaxKeep = NewMaxKeep;
+ cmd.Prune();
}
Version GetVersion()
{
- return Version("WHOWAS Command", VF_VENDOR);
+ return Version("WHOWAS", VF_VENDOR);
}
};
diff --git a/src/commands/cmd_zline.cpp b/src/commands/cmd_zline.cpp
index 91d9c6255..fdb156e0a 100644
--- a/src/commands/cmd_zline.cpp
+++ b/src/commands/cmd_zline.cpp
@@ -49,7 +49,7 @@ CmdResult CommandZline::Handle (const std::vector<std::string>& parameters, User
{
if (target.find('!') != std::string::npos)
{
- user->WriteServ("NOTICE %s :*** You cannot include a nickname in a zline, a zline must ban only an IP mask",user->nick.c_str());
+ user->WriteNotice("*** You cannot include a nickname in a zline, a zline must ban only an IP mask");
return CMD_FAILURE;
}
@@ -72,8 +72,7 @@ CmdResult CommandZline::Handle (const std::vector<std::string>& parameters, User
if (ServerInstance->IPMatchesEveryone(ipaddr,user))
return CMD_FAILURE;
- long duration = ServerInstance->Duration(parameters[1].c_str());
-
+ unsigned long duration = InspIRCd::Duration(parameters[1]);
ZLine* zl = new ZLine(ServerInstance->Time(), duration, user->nick.c_str(), parameters[2].c_str(), ipaddr);
if (ServerInstance->XLines->AddLine(zl,user))
{
@@ -93,7 +92,7 @@ CmdResult CommandZline::Handle (const std::vector<std::string>& parameters, User
else
{
delete zl;
- user->WriteServ("NOTICE %s :*** Z-Line for %s already exists",user->nick.c_str(),ipaddr);
+ user->WriteNotice("*** Z-Line for " + std::string(ipaddr) + " already exists");
}
}
else
@@ -104,7 +103,7 @@ CmdResult CommandZline::Handle (const std::vector<std::string>& parameters, User
}
else
{
- user->WriteServ("NOTICE %s :*** Z-Line %s not found in list, try /stats Z.",user->nick.c_str(),target.c_str());
+ user->WriteNotice("*** Z-Line " + target + " not found in list, try /stats Z.");
return CMD_FAILURE;
}
}