diff options
Diffstat (limited to 'include')
90 files changed, 10336 insertions, 4957 deletions
diff --git a/include/modes/cmode_o.h b/include/aligned_storage.h index c5f1764c1..7bf0fe0a3 100644 --- a/include/modes/cmode_o.h +++ b/include/aligned_storage.h @@ -1,7 +1,7 @@ /* * InspIRCd -- Internet Relay Chat Daemon * - * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org> + * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com> * * 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 @@ -17,21 +17,34 @@ */ -#include "mode.h" -#include "channels.h" +#pragma once -class InspIRCd; +namespace insp +{ + template <typename T> class aligned_storage; +} -/** Channel mode +o - */ -class ModeChannelOp : public ModeHandler +template <typename T> +class insp::aligned_storage { - private: + mutable typename TR1NS::aligned_storage<sizeof(T), TR1NS::alignment_of<T>::value>::type data; + public: - ModeChannelOp(); - ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding); - unsigned int GetPrefixRank(); - void RemoveMode(Channel* channel, irc::modestacker* stack = NULL); - void RemoveMode(User* user, irc::modestacker* stack = NULL); -}; + aligned_storage() + { + } + aligned_storage(const aligned_storage& other) + { + } + + T* operator->() const + { + return static_cast<T*>(static_cast<void*>(&data)); + } + + operator T*() const + { + return operator->(); + } +}; diff --git a/include/bancache.h b/include/bancache.h index a7aac7f17..6e19e1ebe 100644 --- a/include/bancache.h +++ b/include/bancache.h @@ -18,8 +18,7 @@ */ -#ifndef BANCACHE_H -#define BANCACHE_H +#pragma once /** Stores a cached ban entry. * Each ban has one of these hashed in a hash_map to make for faster removal @@ -37,68 +36,42 @@ class CoreExport BanCacheHit /** Reason, shown as quit message */ std::string Reason; - /** IP to match against, no wildcards here (of course) - */ - std::string IP; /** Time that the ban expires at */ time_t Expiry; - BanCacheHit(const std::string &ip, const std::string &type, const std::string &reason) - { - this->Type = type; - this->Reason = reason; - this->IP = ip; - this->Expiry = ServerInstance->Time() + 86400; // a day. this might seem long, but entries will be removed as glines/etc expire. - } + BanCacheHit(const std::string& type, const std::string& reason, time_t seconds); - // overridden to allow custom time - BanCacheHit(const std::string &ip, const std::string &type, const std::string &reason, time_t seconds) - { - this->Type = type; - this->Reason = reason; - this->IP = ip; - this->Expiry = ServerInstance->Time() + seconds; - } + bool IsPositive() const { return (!Reason.empty()); } }; -/* A container of ban cache items. - * must be defined after class BanCacheHit. - */ -typedef nspace::hash_map<std::string, BanCacheHit*, nspace::hash<std::string> > BanCacheHash; - /** A manager for ban cache, which allocates and deallocates and checks cached bans. */ class CoreExport BanCacheManager { - private: - BanCacheHash* BanHash; + /** A container of ban cache items. + */ + typedef TR1NS::unordered_map<std::string, BanCacheHit*, TR1NS::hash<std::string> > BanCacheHash; + + BanCacheHash BanHash; + bool RemoveIfExpired(BanCacheHash::iterator& it); + public: /** Creates and adds a Ban Cache item. * @param ip The IP the item is for. * @param type The type of ban cache item. std::string. .empty() means it's a negative match (user is allowed freely). * @param reason The reason for the ban. Left .empty() if it's a negative match. + * @param seconds Number of seconds before nuking the bancache entry, the default is a day. This might seem long, but entries will be removed as glines/etc expire. */ - BanCacheHit *AddHit(const std::string &ip, const std::string &type, const std::string &reason); - - // Overridden to allow an optional number of seconds before expiry - BanCacheHit *AddHit(const std::string &ip, const std::string &type, const std::string &reason, time_t seconds); + BanCacheHit *AddHit(const std::string &ip, const std::string &type, const std::string &reason, time_t seconds = 0); BanCacheHit *GetHit(const std::string &ip); - bool RemoveHit(BanCacheHit *b); /** Removes all entries of a given type, either positive or negative. Returns the number of hits removed. * @param type The type of bancache entries to remove (e.g. 'G') * @param positive Remove either positive (true) or negative (false) hits. */ - unsigned int RemoveEntries(const std::string &type, bool positive); + void RemoveEntries(const std::string& type, bool positive); - BanCacheManager() - { - this->BanHash = new BanCacheHash(); - } ~BanCacheManager(); - void RehashCache(); }; - -#endif diff --git a/include/base.h b/include/base.h index 19222a6f5..d8781f796 100644 --- a/include/base.h +++ b/include/base.h @@ -20,8 +20,7 @@ */ -#ifndef BASE_H -#define BASE_H +#pragma once #include <map> #include <deque> @@ -180,21 +179,23 @@ class reference */ class CoreExport CoreException : public std::exception { - public: + protected: /** Holds the error message to be displayed */ const std::string err; /** Source of the exception */ const std::string source; - /** Default constructor, just uses the error mesage 'Core threw an exception'. - */ - CoreException() : err("Core threw an exception"), source("The core") {} + + public: /** This constructor can be used to specify an error message before throwing. + * @param message Human readable error message */ CoreException(const std::string &message) : err(message), source("The core") {} /** This constructor can be used to specify an error message before throwing, * and to specify the source of the exception. + * @param message Human readable error message + * @param src Source of the exception */ CoreException(const std::string &message, const std::string &src) : err(message), source(src) {} /** This destructor solves world hunger, cancels the world debt, and causes the world to end. @@ -203,17 +204,14 @@ class CoreExport CoreException : public std::exception */ virtual ~CoreException() throw() {} /** Returns the reason for the exception. - * The module should probably put something informative here as the user will see this upon failure. + * @return Human readable description of the error */ - virtual const char* GetReason() - { - return err.c_str(); - } + const std::string& GetReason() const { return err; } - virtual const char* GetSource() - { - return source.c_str(); - } + /** Returns the source of the exception + * @return Source of the exception + */ + const std::string& GetSource() const { return source; } }; class Module; @@ -237,7 +235,9 @@ enum ServiceType { /** is a data processing provider (MD5, SQL) */ SERVICE_DATA, /** is an I/O hook provider (SSL) */ - SERVICE_IOHOOK + SERVICE_IOHOOK, + /** Service managed by a module */ + SERVICE_CUSTOM }; /** A structure defining something that a module can provide */ @@ -250,10 +250,14 @@ class CoreExport ServiceProvider : public classbase const std::string name; /** Type of service (must match object type) */ const ServiceType service; - ServiceProvider(Module* Creator, const std::string& Name, ServiceType Type) - : creator(Creator), name(Name), service(Type) {} + ServiceProvider(Module* Creator, const std::string& Name, ServiceType Type); virtual ~ServiceProvider(); -}; + /** Register this service in the appropriate registrar + */ + virtual void RegisterService(); -#endif + /** If called, this ServiceProvider won't be registered automatically + */ + void DisableAutoRegister(); +}; diff --git a/include/caller.h b/include/caller.h deleted file mode 100644 index 40574771e..000000000 --- a/include/caller.h +++ /dev/null @@ -1,283 +0,0 @@ -/* - * InspIRCd -- Internet Relay Chat Daemon - * - * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org> - * Copyright (C) 2007 Craig Edwards <craigedwards@brainbox.cc> - * - * 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/>. - */ - - -#ifndef CALLER_H -#define CALLER_H - -/** The templates below can be auto generated by tools/create_templates.pl. - * They are used to represent a functor with a given number of parameters and - * a specific return type. To prevent passing the wrong number of parameters - * and have the compiler detect this error at build-time, each class is numbered - * according to the number of parameters it takes, e.g. caller0, caller1, caller2. - * These have been generated from zero parameters to eight. - * - * If you want to declare a functor which takes two parameters, a User and a Channel, - * and returns bool, simply create it like this: - * - * caller2<bool, User*, Channel*> MyFunction; - * - * and initialize it correctly, when placed into a class you will be able to call it: - * - * bool n = someclass->MyFunction(someuser, somechan); - * - * These functor templates work this way so that you can simply and easily allow - * for these class methods to be overridden from within a module, e.g. have a module - * which completely replaces the code for IsNick, etc. For example, with the example - * above: - * - * MyNewFunction replaceme(ServerInstance); - * - * someclass->MyFunction = \&replaceme; - * - * After this point, calls to someclass->MyFunction will call the new code in your - * replacement functor. - * - * This is a very powerful feature which should be considered 'advanced' and not for - * beginners. If you do not understand these templates, STAY AWAY from playing with - * this until you do, as if you get this wrong, this can generate some pretty long - * winded and confusing error messages at compile time. - */ -template <typename ReturnType> class CoreExport HandlerBase0 : public classbase -{ - public: - virtual ReturnType Call() = 0; - virtual ~HandlerBase0() { } -}; - -template <typename ReturnType, typename Param1> class CoreExport HandlerBase1 : public classbase -{ - public: - virtual ReturnType Call(Param1) = 0; - virtual ~HandlerBase1() { } -}; - -template <typename ReturnType, typename Param1, typename Param2> class CoreExport HandlerBase2 : public classbase -{ - public: - virtual ReturnType Call(Param1, Param2) = 0; - virtual ~HandlerBase2() { } -}; - -template <typename ReturnType, typename Param1, typename Param2, typename Param3> class CoreExport HandlerBase3 : public classbase -{ - public: - virtual ReturnType Call(Param1, Param2, Param3) = 0; - virtual ~HandlerBase3() { } -}; - -template <typename ReturnType, typename Param1, typename Param2, typename Param3, typename Param4> class CoreExport HandlerBase4 : public classbase -{ - public: - virtual ReturnType Call(Param1, Param2, Param3, Param4) = 0; - virtual ~HandlerBase4() { } -}; - -template <typename ReturnType, typename Param1, typename Param2, typename Param3, typename Param4, typename Param5> class CoreExport HandlerBase5 : public classbase -{ - public: - virtual ReturnType Call(Param1, Param2, Param3, Param4, Param5) = 0; - virtual ~HandlerBase5() { } -}; - -template <typename ReturnType, typename Param1, typename Param2, typename Param3, typename Param4, typename Param5, typename Param6> class CoreExport HandlerBase6 : public classbase -{ - public: - virtual ReturnType Call(Param1, Param2, Param3, Param4, Param5, Param6) = 0; - virtual ~HandlerBase6() { } -}; - -template <typename ReturnType, typename Param1, typename Param2, typename Param3, typename Param4, typename Param5, typename Param6, typename Param7> class CoreExport HandlerBase7 : public classbase -{ - public: - virtual ReturnType Call(Param1, Param2, Param3, Param4, Param5, Param6, Param7) = 0; - virtual ~HandlerBase7() { } -}; - -template <typename ReturnType, typename Param1, typename Param2, typename Param3, typename Param4, typename Param5, typename Param6, typename Param7, typename Param8> class CoreExport HandlerBase8 : public classbase -{ - public: - virtual ReturnType Call(Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8) = 0; - virtual ~HandlerBase8() { } -}; - -template <typename HandlerType> class caller -{ - public: - HandlerType* target; - - caller(HandlerType* initial) - : target(initial) - { } - - virtual ~caller() { } -}; - -template <typename ReturnType> class caller0 : public caller< HandlerBase0<ReturnType> > -{ - public: - caller0(HandlerBase0<ReturnType>* initial) - : caller< HandlerBase0<ReturnType> >::caller(initial) - { } - - ReturnType operator() () - { - return this->target->Call(); - } -}; - -template <typename ReturnType, typename Param1> class caller1 : public caller< HandlerBase1<ReturnType, Param1> > -{ - public: - caller1(HandlerBase1<ReturnType, Param1>* initial) - : caller< HandlerBase1<ReturnType, Param1> >(initial) - { } - - ReturnType operator() (Param1 param1) - { - return this->target->Call(param1); - } -}; - -template <typename ReturnType, typename Param1, typename Param2> class caller2 : public caller< HandlerBase2<ReturnType, Param1, Param2> > -{ - public: - caller2(HandlerBase2<ReturnType, Param1, Param2>* initial) - : caller< HandlerBase2<ReturnType, Param1, Param2> >(initial) - { } - - ReturnType operator() (Param1 param1, Param2 param2) - { - return this->target->Call(param1, param2); - } -}; - -template <typename ReturnType, typename Param1, typename Param2, typename Param3> class caller3 : public caller< HandlerBase3<ReturnType, Param1, Param2, Param3> > -{ - public: - caller3(HandlerBase3<ReturnType, Param1, Param2, Param3>* initial) - : caller< HandlerBase3<ReturnType, Param1, Param2, Param3> >(initial) - { } - - ReturnType operator() (Param1 param1, Param2 param2, Param3 param3) - { - return this->target->Call(param1, param2, param3); - } -}; - -template <typename ReturnType, typename Param1, typename Param2, typename Param3, typename Param4> class caller4 : public caller< HandlerBase4<ReturnType, Param1, Param2, Param3, Param4> > -{ - public: - caller4(HandlerBase4<ReturnType, Param1, Param2, Param3, Param4>* initial) - : caller< HandlerBase4<ReturnType, Param1, Param2, Param3, Param4> >(initial) - { } - - ReturnType operator() (Param1 param1, Param2 param2, Param3 param3, Param4 param4) - { - return this->target->Call(param1, param2, param3, param4); - } -}; - -template <typename ReturnType, typename Param1, typename Param2, typename Param3, typename Param4, typename Param5> class caller5 : public caller< HandlerBase5<ReturnType, Param1, Param2, Param3, Param4, Param5> > -{ - public: - caller5(HandlerBase5<ReturnType, Param1, Param2, Param3, Param4, Param5>* initial) - : caller< HandlerBase5<ReturnType, Param1, Param2, Param3, Param4, Param5> >(initial) - { } - - ReturnType operator() (Param1 param1, Param2 param2, Param3 param3, Param4 param4, Param5 param5) - { - return this->target->Call(param1, param2, param3, param4, param5); - } -}; - -template <typename ReturnType, typename Param1, typename Param2, typename Param3, typename Param4, typename Param5, typename Param6> class caller6 : public caller< HandlerBase6<ReturnType, Param1, Param2, Param3, Param4, Param5, Param6> > -{ - public: - caller6(HandlerBase6<ReturnType, Param1, Param2, Param3, Param4, Param5, Param6>* initial) - : caller< HandlerBase6<ReturnType, Param1, Param2, Param3, Param4, Param5, Param6> >(initial) - { } - - ReturnType operator() (Param1 param1, Param2 param2, Param3 param3, Param4 param4, Param5 param5, Param6 param6) - { - return this->target->Call(param1, param2, param3, param4, param5, param6); - } -}; - -template <typename ReturnType, typename Param1, typename Param2, typename Param3, typename Param4, typename Param5, typename Param6, typename Param7> class caller7 : public caller< HandlerBase7<ReturnType, Param1, Param2, Param3, Param4, Param5, Param6, Param7> > -{ - public: - caller7(HandlerBase7<ReturnType, Param1, Param2, Param3, Param4, Param5, Param6, Param7>* initial) - : caller< HandlerBase7<ReturnType, Param1, Param2, Param3, Param4, Param5, Param6, Param7> >(initial) - { } - - ReturnType operator() (Param1 param1, Param2 param2, Param3 param3, Param4 param4, Param5 param5, Param6 param6, Param7 param7) - { - return this->target->Call(param1, param2, param3, param4, param5, param6, param7); - } -}; - -template <typename ReturnType, typename Param1, typename Param2, typename Param3, typename Param4, typename Param5, typename Param6, typename Param7, typename Param8> class caller8 : public caller< HandlerBase8<ReturnType, Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8> > -{ - public: - caller8(HandlerBase8<ReturnType, Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8>* initial) - : caller< HandlerBase8<ReturnType, Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8> >(initial) - { } - - ReturnType operator() (Param1 param1, Param2 param2, Param3 param3, Param4 param4, Param5 param5, Param6 param6, Param7 param7, Param8 param8) - { - return this->target->Call(param1, param2, param3, param4, param5, param6, param7, param8); - } -}; - -/** These shorthand macros are used to define a functor class which only implements Call(). Most functors are like this. - * If you want something more complex, define them by hand. - * - * The first parameter to each macro is the class name to define, the second parameter is the return value of Call(). - * The following parameters are the parameter types for Call(), and again, the macro is numbered to match the number of - * parameters, to prevent mistakes. - */ -#define DEFINE_HANDLER0(NAME, RETURN) \ - class CoreExport NAME : public HandlerBase0<RETURN> { public: NAME() { } virtual ~NAME() { } virtual RETURN Call(); } - -#define DEFINE_HANDLER1(NAME, RETURN, V1) \ - class CoreExport NAME : public HandlerBase1<RETURN, V1> { public: NAME() { } virtual ~NAME() { } virtual RETURN Call(V1); } - -#define DEFINE_HANDLER2(NAME, RETURN, V1, V2) \ - class CoreExport NAME : public HandlerBase2<RETURN, V1, V2> { public: NAME() { } virtual ~NAME() { } virtual RETURN Call(V1, V2); } - -#define DEFINE_HANDLER3(NAME, RETURN, V1, V2, V3) \ - class CoreExport NAME : public HandlerBase3<RETURN, V1, V2, V3> { public: NAME() { } virtual ~NAME() { } virtual RETURN Call(V1, V2, V3); } - -#define DEFINE_HANDLER4(NAME, RETURN, V1, V2, V3, V4) \ - class CoreExport NAME : public HandlerBase4<RETURN, V1, V2, V3, V4> { public: NAME() { } virtual ~NAME() { } virtual RETURN Call(V1, V2, V3, V4); } - -#define DEFINE_HANDLER5(NAME, RETURN, V1, V2, V3, V4, V5) \ - class CoreExport NAME : public HandlerBase5<RETURN, V1, V2, V3, V4, V5> { public: NAME() { } virtual ~NAME() { } virtual RETURN Call(V1, V2, V3, V4, V5); } - -#define DEFINE_HANDLER6(NAME, RETURN, V1, V2, V3, V4, V5, V6) \ - class CoreExport NAME : public HandlerBase6<RETURN, V1, V2, V3, V4, V5, V6> { public: NAME() { } virtual ~NAME() { } virtual RETURN Call(V1, V2, V3, V4, V5, V6); } - -#define DEFINE_HANDLER7(NAME, RETURN, V1, V2, V3, V4, V5, V6, V7) \ - class CoreExport NAME : public HandlerBase7<RETURN, V1, V2, V3, V4, V5, V6, V7> { public: NAME() { } virtual ~NAME() { } virtual RETURN Call(V1, V2, V3, V4, V5, V6, V7); } - -#define DEFINE_HANDLER8(NAME, RETURN, V1, V2, V3, V4, V5, V6, V7, V8) \ - class CoreExport NAME : public HandlerBase8<RETURN, V1, V2, V3, V4, V5, V6, V7, V8> { public: NAME() { } virtual ~NAME() { } virtual RETURN Call(V1, V2, V3, V4, V5, V6, V7, V8); } - -#endif diff --git a/include/channels.h b/include/channels.h index dda53f69d..0557a5898 100644 --- a/include/channels.h +++ b/include/channels.h @@ -20,75 +20,66 @@ */ -#ifndef CHANNELS_H -#define CHANNELS_H +#pragma once #include "membership.h" #include "mode.h" +#include "parammode.h" /** Holds an entry for a ban list, exemption list, or invite list. * This class contains a single element in a channel list, such as a banlist. */ -class HostItem -{ - public: - /** Time the item was added - */ - time_t set_time; - /** Who added the item - */ - std::string set_by; - /** The actual item data - */ - std::string data; - - HostItem() { /* stub */ } - virtual ~HostItem() { /* stub */ } -}; - -/** A subclass of HostItem designed to hold channel bans (+b) - */ -class BanItem : public HostItem -{ -}; /** Holds all relevent information for a channel. * This class represents a channel, and contains its name, modes, topic, topic set time, * etc, and an instance of the BanList type. */ -class CoreExport Channel : public Extensible, public InviteBase +class CoreExport Channel : public Extensible { - /** Connect a Channel to a User + public: + /** A map of Memberships on a channel keyed by User pointers */ - static Channel* ForceChan(Channel* Ptr, User* user, const std::string &privs, bool bursting, bool created); + typedef std::map<User*, insp::aligned_storage<Membership> > MemberMap; + private: /** Set default modes for the channel on creation */ void SetDefaultModes(); - /** Maximum number of bans (cached) - */ - int maxbans; - /** Modes for the channel. - * This is not a null terminated string! It is a bitset where - * each item in it represents if a mode is set. For example - * for mode +A, index 0. Use modechar-65 to calculate which - * field to check. + * It is a bitset where each item in it represents if a mode is set. + * To see if a mode is set, inspect modes[mh->modeid] */ - std::bitset<64> modes; + std::bitset<ModeParser::MODEID_MAX> modes; - /** Parameters for custom modes. - * One for each custom mode letter. + /** Remove the given membership from the channel's internal map of + * memberships and destroy the Membership object. + * This function does not remove the channel from User::chanlist. + * Since the parameter is an iterator to the target, the complexity + * of this function is constant. + * @param membiter The MemberMap iterator to remove, must be valid */ - CustomModeList custom_mode_params; + void DelUser(const MemberMap::iterator& membiter); public: /** Creates a channel record and initialises it with default values - * @throw Nothing at present. + * @param name The name of the channel + * @param ts The creation time of the channel + * @throw CoreException if this channel name is in use */ Channel(const std::string &name, time_t ts); + /** Checks whether the channel should be destroyed, and if yes, begins + * the teardown procedure. + * + * If there are users on the channel or a module vetoes the deletion + * (OnPreChannelDelete hook) then nothing else happens. + * Otherwise, first the OnChannelDelete event is fired, then the channel is + * removed from the channel list. All pending invites are destroyed and + * finally the channel is added to the cull list. + */ + void CheckDestroy(); + /** The channel's name. */ std::string name; @@ -99,7 +90,7 @@ class CoreExport Channel : public Extensible, public InviteBase /** User list. */ - UserMembList userlist; + MemberMap userlist; /** Channel topic. * If this is an empty string, no channel topic is set. @@ -116,32 +107,19 @@ class CoreExport Channel : public Extensible, public InviteBase */ std::string setby; /* 128 */ - /** The list of all bans set on the channel. - */ - BanList bans; - /** Sets or unsets a custom mode in the channels info * @param mode The mode character to set or unset * @param value True if you want to set the mode or false if you want to remove it */ void SetMode(ModeHandler* mode, bool value); - void SetMode(char mode,bool mode_on); - - /** Sets or unsets a custom mode in the channels info - * @param mode The mode character to set or unset - * @param parameter The parameter string to associate with this mode character. - * If it is empty, the mode is unset; if it is nonempty, the mode is set. - */ - void SetModeParam(ModeHandler* mode, const std::string& parameter); - void SetModeParam(char mode, const std::string& parameter); /** Returns true if a mode is set on a channel * @param mode The mode character you wish to query * @return True if the custom mode is set, false if otherwise */ - inline bool IsModeSet(char mode) { return modes[mode-'A']; } - inline bool IsModeSet(ModeHandler* mode) { return modes[mode->GetModeChar()-'A']; } - + bool IsModeSet(ModeHandler* mode) { return ((mode->GetId() != ModeParser::MODEID_MAX) && (modes[mode->GetId()])); } + bool IsModeSet(ModeHandler& mode) { return IsModeSet(&mode); } + bool IsModeSet(ChanModeReference& mode); /** Returns the parameter for a custom mode on a channel. * @param mode The mode character you wish to query @@ -153,24 +131,25 @@ class CoreExport Channel : public Extensible, public InviteBase * * @return The parameter for this mode is returned, or an empty string */ - std::string GetModeParameter(char mode); std::string GetModeParameter(ModeHandler* mode); + std::string GetModeParameter(ChanModeReference& mode); + std::string GetModeParameter(ParamModeBase* pm); /** Sets the channel topic. - * @param u The user setting the topic - * @param t The topic to set it to. Non-const, as it may be modified by a hook. - * @param forceset If set to true then all access checks will be bypassed. + * @param user The user setting the topic. + * @param topic The topic to set it to. + * @param topicts Timestamp of the new topic. + * @param setter Setter string, may be used when the original setter is no longer online. + * If omitted or NULL, the setter string is obtained from the user. */ - int SetTopic(User *u, std::string &t, bool forceset = false); + void SetTopic(User* user, const std::string& topic, time_t topicts, const std::string* setter = NULL); /** Obtain the channel "user counter" - * This returns the channel reference counter, which is initialized - * to 0 when the channel is created and incremented/decremented - * upon joins, parts quits and kicks. + * This returns the number of users on this channel * * @return The number of users on this channel */ - long GetUserCounter(); + size_t GetUserCounter() const { return userlist.size(); } /** Add a user pointer to the internal reference list * @param user The user to add @@ -196,7 +175,7 @@ class CoreExport Channel : public Extensible, public InviteBase * * @return This function returns pointer to a map of User pointers (CUList*). */ - const UserMembList* GetUsers(); + const MemberMap& GetUsers() const { return userlist; } /** Returns true if the user given is on the given channel. * @param user The user to look for @@ -208,141 +187,74 @@ class CoreExport Channel : public Extensible, public InviteBase /** Make src kick user from this channel with the given reason. * @param src The source of the kick - * @param user The user being kicked (must be on this channel) + * @param victimiter Iterator to the user being kicked, must be valid + * @param reason The reason for the kick + */ + void KickUser(User* src, const MemberMap::iterator& victimiter, const std::string& reason); + + /** Make src kick user from this channel with the given reason. + * @param src The source of the kick + * @param user The user being kicked * @param reason The reason for the kick */ - void KickUser(User *src, User *user, const char* reason); + void KickUser(User* src, User* user, const std::string& reason) + { + MemberMap::iterator it = userlist.find(user); + if (it != userlist.end()) + KickUser(src, it, reason); + } /** Part a user from this channel with the given reason. * If the reason field is NULL, no reason will be sent. * @param user The user who is parting (must be on this channel) * @param reason The part reason + * @return True if the user was on the channel and left, false if they weren't and nothing happened */ - void PartUser(User *user, std::string &reason); + bool PartUser(User* user, std::string& reason); - /* Join a user to a channel. May be a channel that doesnt exist yet. + /** Join a local user to a channel, with or without permission checks. May be a channel that doesn't exist yet. * @param user The user to join to the channel. - * @param cn The channel name to join to. Does not have to exist. + * @param channame The channel name to join to. Does not have to exist. * @param key The key of the channel, if given * @param override If true, override all join restrictions such as +bkil * @return A pointer to the Channel the user was joined to. A new Channel may have * been created if the channel did not exist before the user was joined to it. - * If the user could not be joined to a channel, the return value may be NULL. - */ - static Channel* JoinUser(User *user, const char* cn, bool override, const char* key, bool bursting, time_t TS = 0); - - /** Write to a channel, from a user, using va_args for text - * @param user User whos details to prefix the line with - * @param text A printf-style format string which builds the output line without prefix - * @param ... Zero or more POD types - */ - void WriteChannel(User* user, const char* text, ...) CUSTOM_PRINTF(3, 4); - - /** Write to a channel, from a user, using std::string for text - * @param user User whos details to prefix the line with - * @param text A std::string containing the output line without prefix + * If the user could not be joined to a channel, the return value is NULL. */ - void WriteChannel(User* user, const std::string &text); + static Channel* JoinUser(LocalUser* user, std::string channame, bool override = false, const std::string& key = ""); - /** Write to a channel, from a server, using va_args for text - * @param ServName Server name to prefix the line with - * @param text A printf-style format string which builds the output line without prefix - * @param ... Zero or more POD type + /** Join a user to an existing channel, without doing any permission checks + * @param user The user to join to the channel + * @param privs Priviliges (prefix mode letters) to give to this user, may be NULL + * @param bursting True if this join is the result of a netburst (passed to modules in the OnUserJoin hook) + * @param created_by_local True if this channel was just created by a local user (passed to modules in the OnUserJoin hook) + * @return A newly created Membership object, or NULL if the user was already inside the channel or if the user is a server user */ - void WriteChannelWithServ(const std::string& ServName, const char* text, ...) CUSTOM_PRINTF(3, 4); + Membership* ForceJoin(User* user, const std::string* privs = NULL, bool bursting = false, bool created_by_local = false); - /** Write to a channel, from a server, using std::string for text - * @param ServName Server name to prefix the line with - * @param text A std::string containing the output line without prefix - */ - void WriteChannelWithServ(const std::string& ServName, const std::string &text); - - /** Write to all users on a channel except a specific user, using va_args for text. - * Internally, this calls WriteAllExcept(). - * @param user User whos details to prefix the line with, and to omit from receipt of the message - * @param serversource If this parameter is true, use the local server name as the source of the text, otherwise, - * use the nick!user\@host of the user. - * @param status The status of the users to write to, e.g. '@' or '%'. Use a value of 0 to write to everyone - * @param text A printf-style format string which builds the output line without prefix - * @param ... Zero or more POD type - */ - void WriteAllExceptSender(User* user, bool serversource, char status, const char* text, ...) CUSTOM_PRINTF(5, 6); - - /** Write to all users on a channel except a list of users, using va_args for text - * @param user User whos details to prefix the line with, and to omit from receipt of the message - * @param serversource If this parameter is true, use the local server name as the source of the text, otherwise, - * use the nick!user\@host of the user. - * @param status The status of the users to write to, e.g. '@' or '%'. Use a value of 0 to write to everyone - * @param except_list A list of users NOT to send the text to - * @param text A printf-style format string which builds the output line without prefix - * @param ... Zero or more POD type - */ - void WriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const char* text, ...) CUSTOM_PRINTF(6, 7); - - /** Write to all users on a channel except a specific user, using std::string for text. - * Internally, this calls WriteAllExcept(). - * @param user User whos details to prefix the line with, and to omit from receipt of the message - * @param serversource If this parameter is true, use the local server name as the source of the text, otherwise, - * use the nick!user\@host of the user. + /** Write to all users on a channel except some users + * @param protoev Event to send, may contain any number of messages. * @param status The status of the users to write to, e.g. '@' or '%'. Use a value of 0 to write to everyone * @param text A std::string containing the output line without prefix + * @param except_list List of users not to send to */ - void WriteAllExceptSender(User* user, bool serversource, char status, const std::string& text); + void Write(ClientProtocol::Event& protoev, char status = 0, const CUList& except_list = CUList()); - /** Write to all users on a channel except a list of users, using std::string for text - * @param user User whos details to prefix the line with, and to omit from receipt of the message - * @param serversource If this parameter is true, use the local server name as the source of the text, otherwise, - * use the nick!user\@host of the user. + /** Write to all users on a channel except some users. + * @param protoevprov Protocol event provider for the message. + * @param msg Message to send. * @param status The status of the users to write to, e.g. '@' or '%'. Use a value of 0 to write to everyone - * @param except_list A list of users NOT to send the text to * @param text A std::string containing the output line without prefix + * @param except_list List of users not to send to */ - void WriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const std::string& text); - /** Write a line of text that already includes the source */ - void RawWriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const std::string& text); - - /** Returns the maximum number of bans allowed to be set on this channel - * @return The maximum number of bans allowed - */ - long GetMaxBans(); + void Write(ClientProtocol::EventProvider& protoevprov, ClientProtocol::Message& msg, char status = 0, const CUList& except_list = CUList()); /** Return the channel's modes with parameters. * @param showkey If this is set to true, the actual key is shown, * otherwise it is replaced with '<KEY>' * @return The channel mode string */ - char* ChanModes(bool showkey); - - /** Spool the NAMES list for this channel to the given user - * @param user The user to spool the NAMES list to - */ - void UserList(User *user); - - /** Get the number of invisible users on this channel - * @return Number of invisible users - */ - int CountInvisible(); - - /** Get a users prefix on this channel in a string. - * @param user The user to look up - * @return A character array containing the prefix string. - * Unlike GetStatus and GetStatusFlags which will only return the - * core specified modes @, % and + (op, halfop and voice), GetPrefixChar - * will also return module-defined prefixes. If the user has to prefix, - * an empty but non-null string is returned. If the user has multiple - * prefixes, the highest is returned. If you do not recognise the prefix - * character you can get, you can deal with it in a 'proprtional' manner - * compared to known prefixes, using GetPrefixValue(). - */ - const char* GetPrefixChar(User *user); - - /** Return all of a users mode prefixes into a char* string. - * @param user The user to look up - * @return A list of all prefix characters. The prefixes will always - * be in rank order, greatest first, as certain IRC clients require - * this when multiple prefixes are used names lists. - */ - const char* GetAllPrefixChars(User* user); + const char* ChanModes(bool showkey); /** Get the value of a users prefix on this channel. * @param user The user to look up @@ -357,24 +269,6 @@ class CoreExport Channel : public Extensible, public InviteBase */ unsigned int GetPrefixValue(User* user); - /** This method removes all prefix characters from a user. - * It will not inform the user or the channel of the removal of prefixes, - * and should be used when the user parts or quits. - * @param user The user to remove all prefixes from - */ - void RemoveAllPrefixes(User* user); - - /** Add a prefix character to a user. - * Only the core should call this method, usually from - * within the mode parser or when the first user joins - * the channel (to grant ops to them) - * @param user The user to associate the privilage with - * @param prefix The prefix character to associate - * @param adding True if adding the prefix, false when removing - * @return True if a change was made - */ - bool SetPrefix(User* user, char prefix, bool adding); - /** Check if a user is banned on this channel * @param user A user to check against the banlist * @returns True if the user given is banned @@ -389,9 +283,44 @@ class CoreExport Channel : public Extensible, public InviteBase */ ModResult GetExtBanStatus(User *u, char type); - /** Clears the cached max bans value + /** Write a NOTICE to all local users on the channel + * @param text Text to send */ - void ResetMaxBans(); + void WriteNotice(const std::string& text); }; -#endif +inline bool Channel::HasUser(User* user) +{ + return (userlist.find(user) != userlist.end()); +} + +inline std::string Channel::GetModeParameter(ChanModeReference& mode) +{ + if (!mode) + return ""; + return GetModeParameter(*mode); +} + +inline std::string Channel::GetModeParameter(ModeHandler* mh) +{ + std::string out; + ParamModeBase* pm = mh->IsParameterMode(); + if (pm && this->IsModeSet(pm)) + pm->GetParameter(this, out); + return out; +} + +inline std::string Channel::GetModeParameter(ParamModeBase* pm) +{ + std::string out; + if (this->IsModeSet(pm)) + pm->GetParameter(this, out); + return out; +} + +inline bool Channel::IsModeSet(ChanModeReference& mode) +{ + if (!mode) + return false; + return IsModeSet(*mode); +} diff --git a/include/clientprotocol.h b/include/clientprotocol.h new file mode 100644 index 000000000..a3efcf984 --- /dev/null +++ b/include/clientprotocol.h @@ -0,0 +1,726 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com> + * + * 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/>. + */ + + +#pragma once + +#include "event.h" + +namespace ClientProtocol +{ + class EventHook; + class MessageSource; + struct RFCEvents; + struct ParseOutput; + class TagSelection; +} + +/** Contains a message parsed from wire format. + * Used by Serializer::Parse(). + */ +struct ClientProtocol::ParseOutput +{ + /** Command name, must not be empty. + */ + std::string cmd; + + /** Parameter list, may be empty. + */ + ClientProtocol::ParamList params; + + /** Message tags, may be empty. + */ + ClientProtocol::TagMap tags; +}; + +/** A selection of zero or more tags in a TagMap. + */ +class ClientProtocol::TagSelection +{ + std::bitset<64> selection; + + public: + /** Check if a tag is selected. + * @param tags TagMap the tag is in. The TagMap must contain the same tags as it had when the tag + * was selected with Select(), otherwise the result is not meaningful. + * @param it Iterator to the tag to check. + * @return True if the tag is selected, false otherwise. + */ + bool IsSelected(const TagMap& tags, TagMap::const_iterator it) const + { + const size_t index = std::distance(tags.begin(), it); + return ((index < selection.size()) && (selection[index])); + } + + /** Select a tag. + * @param tags TagMap the tag is in. This parameter must be the same every time the method is called. + * The TagMap must not be altered otherwise the results of IsSelected() is not meaningful. + * @param it Iterator to the tag to mark as selected. + */ + void Select(const TagMap& tags, TagMap::const_iterator it) + { + const size_t index = std::distance(tags.begin(), it); + if (index < selection.size()) + selection[index] = true; + } + + /** Check if a TagSelection is equivalent to this object. + * @param other Other TagSelection object to compare this with. + * @return True if the objects are equivalent, false if they aren't. + */ + bool operator==(const TagSelection& other) const + { + return (this->selection == other.selection); + } +}; + +class ClientProtocol::MessageSource +{ + User* sourceuser; + const std::string* sourcestr; + + public: + /** Constructor, sets the source to be the full host of a user or sets it to be nothing. + * The actual source string when serializing will be obtained from User::GetFullHost() if the user is non-NULL. + * @param Sourceuser User to set as source of the message. If NULL, the message won't have a source when serialized. + * Optional, defaults to NULL. + */ + MessageSource(User* Sourceuser = NULL) + { + SetSourceUser(Sourceuser); + } + + /** Constructor, sets the source to the supplied string and optionally sets the source user. + * @param Sourcestr String to use as message source for serialization purposes. Must remain valid + * as long as this object is alive. + * @param Sourceuser User to set as source. Optional, defaults to NULL. It will not be used for serialization but + * if provided it may be used internally, for example to create message tags. + * Useful when the source string is synthesized but it is still related to a User. + */ + MessageSource(const std::string& Sourcestr, User* Sourceuser = NULL) + { + SetSource(Sourcestr, Sourceuser); + } + + /** Get the source of this message as a string. + * @return Pointer to the message source string or NULL if there is no source. + */ + const std::string* GetSource() const + { + // Return string if there's one explicitly set + if (sourcestr) + return sourcestr; + if (sourceuser) + return &sourceuser->GetFullHost(); + return NULL; + } + + /** Get the source User. + * This shouldn't be used for serialization, use GetSource() for that. + * @return User pointer if the message has a source user, NULL otherwise. + */ + User* GetSourceUser() const { return sourceuser; } + + /** Set the source of this message to a User. + * See the one parameter constructor for a more detailed description. + * @param Sourceuser User to set as source. + */ + void SetSourceUser(User* Sourceuser) + { + sourceuser = Sourceuser; + sourcestr = NULL; + } + + /** Set the source string and optionally source user. + * See the two parameter constructor for a more detailed description. + * @param Sourcestr String source, to be used for serialization purposes. Must remain valid as long + * as this object is alive. + * @param Sourceuser Source user to set, optional. + */ + void SetSource(const std::string& Sourcestr, User* Sourceuser = NULL) + { + sourcestr = &Sourcestr; + sourceuser = Sourceuser; + } + + /** Copy the source from a MessageSource object. + * @param other MessageSource object to copy from. + */ + void SetSource(const MessageSource& other) + { + sourcestr = other.sourcestr; + sourceuser = other.sourceuser; + } +}; + +/** Outgoing client protocol message. + * Represents a high level client protocol message which is turned into raw or wire format + * by a Serializer. Messages can be serialized into different format by different serializers. + * + * Messages are created on demand and are disposed of after they have been sent. + * + * All messages have a command name, a list of parameters and a map of tags, the last two can be empty. + * They also always have a source, see class MessageSource. + */ +class ClientProtocol::Message : public ClientProtocol::MessageSource +{ + public: + /** Contains information required to identify a specific version of a serialized message. + */ + struct SerializedInfo + { + const Serializer* serializer; + TagSelection tagwl; + + /** Constructor. + * @param Ser Serializer used to serialize the message. + * @param Tagwl Tag whitelist used to serialize the message. + */ + SerializedInfo(const Serializer* Ser, const TagSelection& Tagwl) + : serializer(Ser) + , tagwl(Tagwl) + { + } + + /** Check if a SerializedInfo object is equivalent to this object. + * @param other Other SerializedInfo object. + * @return True if other is equivalent to this object, false otherwise. + */ + bool operator==(const SerializedInfo& other) const + { + return ((serializer == other.serializer) && (tagwl == other.tagwl)); + } + }; + + class Param + { + const std::string* ptr; + insp::aligned_storage<std::string> str; + bool owned; + + void InitFrom(const Param& other) + { + owned = other.owned; + if (owned) + new(str) std::string(*other.str); + else + ptr = other.ptr; + } + + public: + operator const std::string&() const { return (owned ? *str : *ptr); } + + Param() + : ptr(NULL) + , owned(false) + { + } + + Param(const std::string& s) + : ptr(&s) + , owned(false) + { + } + + Param(int, const char* s) + : owned(true) + { + new(str) std::string(s); + } + + Param(int, const std::string& s) + : owned(true) + { + new(str) std::string(s); + } + + Param(const Param& other) + { + InitFrom(other); + } + + ~Param() + { + using std::string; + if (owned) + str->~string(); + } + + Param& operator=(const Param& other) + { + if (&other == this) + return *this; + + using std::string; + if (owned) + str->~string(); + InitFrom(other); + return *this; + } + + bool IsOwned() const { return owned; } + }; + + typedef std::vector<Param> ParamList; + + private: + typedef std::vector<std::pair<SerializedInfo, SerializedMessage> > SerializedList; + + ParamList params; + TagMap tags; + std::string command; + bool msginit_done; + mutable SerializedList serlist; + bool sideeffect; + + protected: + /** Set command string. + * @param cmd Command string to set. + */ + void SetCommand(const char* cmd) + { + command.clear(); + if (cmd) + command = cmd; + } + + public: + /** Constructor. + * @param cmd Command name, e.g. "JOIN", "NICK". May be NULL. If NULL, the command must be set + * with SetCommand() before the message is serialized. + * @param Sourceuser See the one parameter constructor of MessageSource for description. + */ + Message(const char* cmd, User* Sourceuser = NULL) + : ClientProtocol::MessageSource(Sourceuser) + , command(cmd ? cmd : std::string()) + , msginit_done(false) + , sideeffect(false) + { + params.reserve(8); + serlist.reserve(8); + } + + /** Constructor. + * @param cmd Command name, e.g. "JOIN", "NICK". May be NULL. If NULL, the command must be set + * with SetCommand() before the message is serialized. + * @param Sourcestr See the two parameter constructor of MessageSource for description. + * Must remain valid as long as this object is alive. + * @param Sourceuser See the two parameter constructor of MessageSource for description. + */ + Message(const char* cmd, const std::string& Sourcestr, User* Sourceuser = NULL) + : ClientProtocol::MessageSource(Sourcestr, Sourceuser) + , command(cmd ? cmd : std::string()) + , msginit_done(false) + , sideeffect(false) + { + params.reserve(8); + serlist.reserve(8); + } + + /** Get the parameters of this message. + * @return List of parameters. + */ + const ParamList& GetParams() const { return params; } + + /** Get a map of tags attached to this message. + * The map contains the tag providers that attached the tag to the message. + * @return Map of tags. + */ + const TagMap& GetTags() const { return tags; } + + /** Get the command string. + * @return Command string, e.g. "NICK", "001". + */ + const char* GetCommand() const { return command.c_str(); } + + /** Add a parameter to the parameter list. + * @param str String to add, will be copied. + */ + void PushParam(const char* str) { params.push_back(Param(0, str)); } + + /** Add a parameter to the parameter list. + * @param str String to add, will be copied. + */ + void PushParam(const std::string& str) { params.push_back(Param(0, str)); } + + /** Add a parameter to the parameter list. + * @param str String to add. + * The string will NOT be copied, it must remain alive until ClearParams() is called or until the object is destroyed. + */ + void PushParamRef(const std::string& str) { params.push_back(str); } + + /** Add a placeholder parameter to the parameter list. + * Placeholder parameters must be filled in later with actual parameters using ReplaceParam() or ReplaceParamRef(). + */ + void PushParamPlaceholder() { params.push_back(Param()); } + + /** Replace a parameter or a placeholder that is already in the parameter list. + * @param index Index of the parameter to replace. Must be less than GetParams().size(). + * @param str String to replace the parameter or placeholder with, will be copied. + */ + void ReplaceParam(unsigned int index, const char* str) { params[index] = Param(0, str); } + + /** Replace a parameter or a placeholder that is already in the parameter list. + * @param index Index of the parameter to replace. Must be less than GetParams().size(). + * @param str String to replace the parameter or placeholder with, will be copied. + */ + void ReplaceParam(unsigned int index, const std::string& str) { params[index] = Param(0, str); } + + /** Replace a parameter or a placeholder that is already in the parameter list. + * @param index Index of the parameter to replace. Must be less than GetParams().size(). + * @param str String to replace the parameter or placeholder with. + * The string will NOT be copied, it must remain alive until ClearParams() is called or until the object is destroyed. + */ + void ReplaceParamRef(unsigned int index, const std::string& str) { params[index] = Param(str); } + + /** Add a tag. + * @param tagname Raw name of the tag to use in the protocol. + * @param tagprov Provider of the tag. + * @param val Tag value. If empty no value will be sent with the tag. + * @param tagdata Tag provider specific data, will be passed to MessageTagProvider::ShouldSendTag(). Optional, defaults to NULL. + */ + void AddTag(const std::string& tagname, MessageTagProvider* tagprov, const std::string& val, void* tagdata = NULL) + { + tags.insert(std::make_pair(tagname, MessageTagData(tagprov, val, tagdata))); + } + + /** Add all tags in a TagMap to the tags in this message. Existing tags will not be overwritten. + * @param newtags New tags to add. + */ + void AddTags(const ClientProtocol::TagMap& newtags) + { + tags.insert(newtags.begin(), newtags.end()); + } + + /** Get the message in a serialized form. + * @param serializeinfo Information about which exact serialized form of the message is the caller asking for + * (which serializer to use and which tags to include). + * @return Serialized message according to serializeinfo. The returned reference remains valid until the + * next call to this method. + */ + const SerializedMessage& GetSerialized(const SerializedInfo& serializeinfo) const; + + /** Clear the parameter list and tags. + */ + void ClearParams() + { + msginit_done = false; + params.clear(); + tags.clear(); + InvalidateCache(); + } + + /** Remove all serialized messages. + * If a parameter is changed after the message has been sent at least once, this method must be called before + * serializing the message again to ensure the cache won't contain stale data. + */ + void InvalidateCache() + { + serlist.clear(); + } + + void CopyAll() + { + size_t j = 0; + for (ParamList::iterator i = params.begin(); i != params.end(); ++i, j++) + { + Param& curr = *i; + if (!curr.IsOwned()) + ReplaceParam(j, curr); + } + } + + void SetSideEffect(bool Sideeffect) { sideeffect = Sideeffect; } + bool IsSideEffect() const { return sideeffect; } + + friend class Serializer; +}; + +/** Client protocol event class. + * All messages sent to a user must be part of an event. A single event may result in more than one protocol message + * being sent, for example a join event may result in a JOIN and a MODE protocol message sent to members of the channel + * if the joining user has some prefix modes set. + * + * Event hooks attached to a specific event can alter the messages sent for that event. + */ +class ClientProtocol::Event +{ + EventProvider* event; + Message* initialmsg; + const MessageList* initialmsglist; + bool eventinit_done; + + public: + /** Constructor. + * @param protoevent Protocol event provider the event is an instance of. + */ + Event(EventProvider& protoeventprov) + : event(&protoeventprov) + , initialmsg(NULL) + , initialmsglist(NULL) + , eventinit_done(false) + { + } + + /** Constructor. + * @param protoevent Protocol event provider the event is an instance of. + * @param msg Message to include in this event by default. + */ + Event(EventProvider& protoeventprov, ClientProtocol::Message& msg) + : event(&protoeventprov) + , initialmsg(&msg) + , initialmsglist(NULL) + , eventinit_done(false) + { + } + + /** Set a single message as the initial message in the event. + * Modules may alter this later. + */ + void SetMessage(Message* msg) + { + initialmsg = msg; + initialmsglist = NULL; + } + + /** Set a list of messages as the initial messages in the event. + * Modules may alter this later. + */ + void SetMessageList(const MessageList& msglist) + { + initialmsg = NULL; + initialmsglist = &msglist; + } + + /** Get a list of messages to send to a user. + * The exact messages sent to a user are determined by the initial message(s) set and hooks. + * @param user User to get the messages for. + * @param messagelist List to fill in with messages to send to the user for the event + */ + void GetMessagesForUser(LocalUser* user, MessageList& messagelist); +}; + +/** Base class for message tag providers. + * All message tags belong to a message tag provider. Message tag providers can populate messages + * with tags before the message is sent and they have the job of determining whether a user should + * get a message tag or be allowed to send one. + */ +class ClientProtocol::MessageTagProvider : public Events::ModuleEventListener +{ + public: + /** Constructor. + * @param mod Module owning the provider. + */ + MessageTagProvider(Module* mod) + : Events::ModuleEventListener(mod, "event/messagetag") + { + } + + /** Called when a message is ready to be sent to give the tag provider a chance to add tags to the message. + * To add tags call Message::AddTag(). If the provided tag or tags have been added already elsewhere or if the + * provider doesn't want its tag(s) to be on the message, the implementation doesn't have to do anything special. + * The default implementation does nothing. + * @param msg Message to be populated with tags. + */ + virtual void OnClientProtocolPopulateTags(ClientProtocol::Message& msg) + { + } + + /** Called for each tag that the server receives from a client in a message. + * @param user User that sent the tag. + * @param tagname Name of the tag. + * @param value Value of the tag, empty string if the tag has no value. May be modified. + * @return MOD_RES_ALLOW to accept the tag with the value in 'value', MOD_RES_DENY to reject the tag and act as if it wasn't sent, + * MOD_RES_PASSTHRU to make no decision. If no hooks accept a tag, the tag is rejected. + * The default implementation returns MOD_RES_PASSTHRU. + */ + virtual ModResult OnClientProtocolProcessTag(LocalUser* user, const std::string& tagname, std::string& tagvalue) + { + return MOD_RES_PASSTHRU; + } + + /** Called whenever a user is about to receive a message that has a tag attached which is provided by this provider + * to determine whether or not the user should get the tag. + * @param user User in question. + * @param tagdata Tag in question. + * @return True if the tag should be sent to the user, false otherwise. + */ + virtual bool ShouldSendTag(LocalUser* user, const MessageTagData& tagdata) = 0; +}; + +/** Base class for client protocol event hooks. + * A protocol event hook is attached to a single event type. It has the ability to alter or block messages + * sent to users which belong to the event the hook is attached to. + */ +class ClientProtocol::EventHook : public Events::ModuleEventListener +{ + public: + static std::string GetEventName(const std::string& name) + { + return "event/protoevent_" + name; + } + + /** Constructor. + * @param mod Owner of the hook. + * @param name Name of the event to hook. + * @param priority Priority of the hook. Determines the order in which hooks for the same event get called. + * Optional. + */ + EventHook(Module* mod, const std::string& name, unsigned int priority = Events::ModuleEventListener::DefaultPriority) + : Events::ModuleEventListener(mod, GetEventName(name), priority) + { + } + + /** Called exactly once before an event is sent to any user. + * The default implementation doesn't do anything. + * @param ev Event being sent to one or more users. + */ + virtual void OnEventInit(const ClientProtocol::Event& ev) + { + } + + /** Called for each user that may receive the event. + * The hook may alter the messages sent to the user and decide whether further hooks should be executed. + * @param user User the message list is being prepared to be sent to. + * @param ev Event associated with the messages. + * @param messagelist List of messages to send to the user. The hook can alter this in any way it sees fit, for example it can replace messages, + * add new messages, etc. The list must not be empty when the method returns. + * @return MOD_RES_PASSTHRU to run hooks after the called hook or if no hooks are after the called hook, send the messages in messagelist to the user. + * MOD_RES_DENY to not send any messages to the user and to not run other hooks, + * MOD_RES_ALLOW to send the messages in messagelist to the user and to not run other hooks. + */ + virtual ModResult OnPreEventSend(LocalUser* user, const ClientProtocol::Event& ev, ClientProtocol::MessageList& messagelist) = 0; +}; + +/** Event provider for client protocol events. + * Protocol event hooks can be attached to the instances of these providers. The core has event + * providers for most common IRC events defined in RFC1459. + */ +class ClientProtocol::EventProvider : public Events::ModuleEventProvider +{ + public: + /** Constructor. + * @param Mod Module that owns the event provider. + * @param eventname Name of the event this provider is for, e.g. "JOIN", "PART", "NUMERIC". + * Should match command name if applicable. + */ + EventProvider(Module* Mod, const std::string& eventname) + : Events::ModuleEventProvider(Mod, ClientProtocol::EventHook::GetEventName(eventname)) + { + } +}; + +/** Commonly used client protocol events. + * Available via InspIRCd::GetRFCEvents(). + */ +struct ClientProtocol::RFCEvents +{ + EventProvider numeric; + EventProvider join; + EventProvider part; + EventProvider kick; + EventProvider quit; + EventProvider nick; + EventProvider mode; + EventProvider topic; + EventProvider privmsg; + EventProvider invite; + EventProvider ping; + EventProvider pong; + EventProvider error; + + RFCEvents() + : numeric(NULL, "NUMERIC") + , join(NULL, "JOIN") + , part(NULL, "PART") + , kick(NULL, "KICK") + , quit(NULL, "QUIT") + , nick(NULL, "NICK") + , mode(NULL, "MODE") + , topic(NULL, "TOPIC") + , privmsg(NULL, "PRIVMSG") + , invite(NULL, "INVITE") + , ping(NULL, "PING") + , pong(NULL, "PONG") + , error(NULL, "ERROR") + { + } +}; + +/** Base class for client protocol serializers. + * A serializer has to implement serialization and parsing of protocol messages to/from wire format. + */ +class CoreExport ClientProtocol::Serializer : public DataProvider +{ + Events::ModuleEventProvider evprov; + + /** Make a white list containing which tags a user should get. + * @param user User in question. + * @param tagmap Tag map that contains all possible tags. + * @return Whitelist of tags to send to the user. + */ + TagSelection MakeTagWhitelist(LocalUser* user, const TagMap& tagmap) const; + + public: + /** Constructor. + * @param mod Module owning the serializer. + * @param Name Name of the serializer, e.g. "rfc". + */ + Serializer(Module* mod, const char* Name); + + /** Handle a tag in a message being parsed. Call this method for each parsed tag. + * @param user User sending the tag. + * @param tagname Name of the tag. + * @param tagvalue Tag value, may be empty. + * @param tags TagMap to place the tag into, if it gets accepted. + * @return True if no error occured, false if the tag name is invalid or if this tag already exists. + */ + bool HandleTag(LocalUser* user, const std::string& tagname, std::string& tagvalue, TagMap& tags) const; + + /** Serialize a message for a user. + * @param user User to serialize the message for. + * @param msg Message to serialize. + * @return Raw serialized message, only containing the appropriate tags for the user. + * The reference is guaranteed to be valid as long as the Message object is alive and until the same + * Message is serialized for another user. + */ + const SerializedMessage& SerializeForUser(LocalUser* user, Message& msg); + + /** Serialize a high level protocol message into wire format. + * @param msg High level message to serialize. Contains all necessary information about the message, including all possible tags. + * @param tagwl Message tags to include in the serialized message. Tags attached to the message but not included in the whitelist must not + * appear in the output. This is because each user may get a different set of tags for the same message. + * @return Protocol message in wire format. Must contain message delimiter as well, if any (e.g. CRLF for RFC1459). + */ + virtual std::string Serialize(const Message& msg, const TagSelection& tagwl) const = 0; + + /** Parse a protocol message from wire format. + * @param user Source of the message. + * @param line Raw protocol message. + * @param parseoutput Output of the parser. + * @return True if the message was parsed successfully into parseoutput and should be processed, false to drop the message. + */ + virtual bool Parse(LocalUser* user, const std::string& line, ParseOutput& parseoutput) = 0; +}; + +inline ClientProtocol::MessageTagData::MessageTagData(MessageTagProvider* prov, const std::string& val, void* data) + : tagprov(prov) + , value(val) + , provdata(data) +{ +} diff --git a/include/clientprotocolevent.h b/include/clientprotocolevent.h new file mode 100644 index 000000000..6b89d0f72 --- /dev/null +++ b/include/clientprotocolevent.h @@ -0,0 +1,78 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com> + * + * 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/>. + */ + + +#pragma once + +namespace ClientProtocol +{ + namespace Events + { + struct Join; + class Mode; + } +} + +struct ClientProtocol::Events::Join : public ClientProtocol::Messages::Join, public ClientProtocol::Event +{ + Join() + : ClientProtocol::Event(ServerInstance->GetRFCEvents().join, *this) + { + } + + Join(Membership* Memb) + : ClientProtocol::Messages::Join(Memb) + , ClientProtocol::Event(ServerInstance->GetRFCEvents().join, *this) + { + } + + Join(Membership* Memb, const std::string& Sourcestr) + : ClientProtocol::Messages::Join(Memb, Sourcestr) + , ClientProtocol::Event(ServerInstance->GetRFCEvents().join, *this) + { + } +}; + +class ClientProtocol::Events::Mode : public ClientProtocol::Event +{ + std::list<ClientProtocol::Messages::Mode> modelist; + std::vector<Message*> modemsgplist; + const Modes::ChangeList& modechanges; + + public: + static void BuildMessages(User* source, Channel* Chantarget, User* Usertarget, const Modes::ChangeList& changelist, std::list<ClientProtocol::Messages::Mode>& modelist, std::vector<Message*>& modemsgplist) + { + // Build as many MODEs as necessary + for (Modes::ChangeList::List::const_iterator i = changelist.getlist().begin(); i != changelist.getlist().end(); i = modelist.back().GetEndIterator()) + { + modelist.push_back(ClientProtocol::Messages::Mode(source, Chantarget, Usertarget, changelist, i)); + modemsgplist.push_back(&modelist.back()); + } + } + + Mode(User* source, Channel* Chantarget, User* Usertarget, const Modes::ChangeList& changelist) + : ClientProtocol::Event(ServerInstance->GetRFCEvents().mode) + , modechanges(changelist) + { + BuildMessages(source, Chantarget, Usertarget, changelist, modelist, modemsgplist); + SetMessageList(modemsgplist); + } + + const Modes::ChangeList& GetChangeList() const { return modechanges; } + const std::list<ClientProtocol::Messages::Mode>& GetMessages() const { return modelist; } +}; diff --git a/include/clientprotocolmsg.h b/include/clientprotocolmsg.h new file mode 100644 index 000000000..d1562d7d1 --- /dev/null +++ b/include/clientprotocolmsg.h @@ -0,0 +1,677 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com> + * + * 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/>. + */ + + +#pragma once + +namespace ClientProtocol +{ + namespace Messages + { + class Numeric; + class Join; + struct Part; + struct Kick; + struct Quit; + struct Nick; + class Mode; + struct Topic; + class Privmsg; + struct Invite; + struct Ping; + struct Pong; + struct Error; + } +} + +/** Numeric message. + * Doesn't have a fixed command name, it's always a 3 digit number padded with zeroes if necessary. + * The first parameter is the target of the numeric which is almost always the nick of the user + * the numeric will be sent to. + */ +class ClientProtocol::Messages::Numeric : public ClientProtocol::Message +{ + char numericstr[4]; + + void InitCommand(unsigned int number) + { + snprintf(numericstr, sizeof(numericstr), "%03u", number); + SetCommand(numericstr); + } + + void InitFromNumeric(const ::Numeric::Numeric& numeric) + { + InitCommand(numeric.GetNumeric()); + for (std::vector<std::string>::const_iterator i = numeric.GetParams().begin(); i != numeric.GetParams().end(); ++i) + PushParamRef(*i); + } + + public: + /** Constructor, target is a User. + * @param num Numeric object to send. Must remain valid as long as this object is alive and must not be modified. + * @param user User to send the numeric to. May be unregistered, must remain valid as long as this object is alive. + */ + Numeric(const ::Numeric::Numeric& num, User* user) + : ClientProtocol::Message(NULL, (num.GetServer() ? num.GetServer()->GetName() : ServerInstance->Config->ServerName)) + { + if (user->registered & REG_NICK) + PushParamRef(user->nick); + else + PushParam("*"); + InitFromNumeric(num); + } + + /** Constructor, target is a string. + * @param num Numeric object to send. Must remain valid as long as this object is alive and must not be modified. + * @param target Target string, must stay valid as long as this object is alive. + */ + Numeric(const ::Numeric::Numeric& num, const std::string& target) + : ClientProtocol::Message(NULL, (num.GetServer() ? num.GetServer()->GetName() : ServerInstance->Config->ServerName)) + { + PushParamRef(target); + InitFromNumeric(num); + } + + /** Constructor. Only the numeric number has to be specified. + * @param num Numeric number. + */ + Numeric(unsigned int num) + : ClientProtocol::Message(NULL, ServerInstance->Config->ServerName) + { + InitCommand(num); + PushParam("*"); + } +}; + +/** JOIN message. + * Sent when a user joins a channel. + */ +class ClientProtocol::Messages::Join : public ClientProtocol::Message +{ + Membership* memb; + + public: + /** Constructor. Does not populate parameters, call SetParams() before sending the message. + */ + Join() + : ClientProtocol::Message("JOIN") + , memb(NULL) + { + } + + /** Constructor. + * @param Memb Membership of the joining user. + */ + Join(Membership* Memb) + : ClientProtocol::Message("JOIN", Memb->user) + { + SetParams(Memb); + } + + /** Constructor. + * @param Memb Membership of the joining user. + * @param sourcestrref Message source string, must remain valid as long as this object is alive. + */ + Join(Membership* Memb, const std::string& sourcestrref) + : ClientProtocol::Message("JOIN", sourcestrref, Memb->user) + { + SetParams(Memb); + } + + /** Populate parameters from a Membership + * @param Memb Membership of the joining user. + */ + void SetParams(Membership* Memb) + { + memb = Memb; + PushParamRef(memb->chan->name); + } + + /** Get the Membership of the joining user. + * @return Membership of the joining user. + */ + Membership* GetMember() const { return memb; } +}; + +/** PART message. + * Sent when a user parts a channel. + */ +struct ClientProtocol::Messages::Part : public ClientProtocol::Message +{ + /** Constructor. + * @param memb Member parting. + * @param reason Part reason, may be empty. If non-empty, must remain valid as long as this object is alive. + */ + Part(Membership* memb, const std::string& reason) + : ClientProtocol::Message("PART", memb->user) + { + PushParamRef(memb->chan->name); + if (!reason.empty()) + PushParamRef(reason); + } +}; + +/** KICK message. + * Sent when a user is kicked from a channel. + */ +struct ClientProtocol::Messages::Kick : public ClientProtocol::Message +{ + /** Constructor. + * @param source User that does the kick. + * @param memb Membership of the user being kicked. + * @param reason Kick reason. Must remain valid as long as this object is alive. + */ + Kick(User* source, Membership* memb, const std::string& reason) + : ClientProtocol::Message("KICK", source) + { + PushParamRef(memb->chan->name); + PushParamRef(memb->user->nick); + PushParamRef(reason); + } +}; + +/** QUIT message. + * Sent when a user quits. + */ +struct ClientProtocol::Messages::Quit : public ClientProtocol::Message +{ + /** Constructor. + * @param source User quitting. + * @param reason Quit reason, may be empty. Must remain valid as long as this object is alive. + */ + Quit(User* source, const std::string& reason) + : ClientProtocol::Message("QUIT", source) + { + if (!reason.empty()) + PushParamRef(reason); + } +}; + +/** NICK message. + * Sent when a user changes their nickname. + */ +struct ClientProtocol::Messages::Nick : public ClientProtocol::Message +{ + /** Constructor. + * @param source User changing nicks. + * @param newnick New nickname. Must remain valid as long as this object is alive. + */ + Nick(User* source, const std::string& newnick) + : ClientProtocol::Message("NICK", source) + { + PushParamRef(newnick); + } +}; + +/** MODE message. + * Sent when modes are changed on a user or channel. + */ +class ClientProtocol::Messages::Mode : public ClientProtocol::Message +{ + Channel* chantarget; + User* usertarget; + Modes::ChangeList::List::const_iterator beginit; + Modes::ChangeList::List::const_iterator lastit; + + /** Convert a range of a mode change list to mode letters and '+', '-' symbols. + * @param list Mode change list. + * @param maxlinelen Maximum output length. + * @param beginit Iterator to the first element in 'list' to process. + * @param lastit Iterator which is set to the first element not processed due to length limitations by the method. + */ + static std::string ToModeLetters(const Modes::ChangeList::List& list, std::string::size_type maxlinelen, Modes::ChangeList::List::const_iterator beginit, Modes::ChangeList::List::const_iterator& lastit) + { + std::string ret; + std::string::size_type paramlength = 0; + char output_pm = '\0'; // current output state, '+' or '-' + + Modes::ChangeList::List::const_iterator i; + for (i = beginit; i != list.end(); ++i) + { + const Modes::Change& item = *i; + + const char needed_pm = (item.adding ? '+' : '-'); + if (needed_pm != output_pm) + { + output_pm = needed_pm; + ret.push_back(output_pm); + } + + if (!item.param.empty()) + paramlength += item.param.length() + 1; + if (ret.length() + 1 + paramlength > maxlinelen) + { + // Mode sequence is getting too long + const char c = *ret.rbegin(); + if ((c == '+') || (c == '-')) + ret.erase(ret.size()-1); + break; + } + + ret.push_back(item.mh->GetModeChar()); + } + + lastit = i; + return ret; + } + + /** Push mode parameters for modes that have one, starting at beginit to lastit (not including lastit). + */ + void PushModeParams() + { + for (Modes::ChangeList::List::const_iterator i = beginit; i != lastit; ++i) + { + const Modes::Change& item = *i; + if (!item.param.empty()) + PushParamRef(item.param); + } + } + + public: + /** Convert an entire mode change list into mode letters and '+' and '-' characters. + * @param changelist Mode change list to convert into mode letters. + * @return Mode letters. + */ + static std::string ToModeLetters(const Modes::ChangeList& changelist) + { + // TODO: This assumes that std::string::max_size() >= UINT_MAX + Modes::ChangeList::List::const_iterator dummy; + return ToModeLetters(changelist.getlist(), UINT_MAX, changelist.getlist().begin(), dummy); + } + + /** Constructor, populate parameters starting from a given position in a mode change list. + * @param source User doing the mode change. + * @param Chantarget Channel target of the mode change. May be NULL if Usertarget is non-NULL. + * @param Usertarget User target of the mode change. May be NULL if Chantarget is non-NULL. + * @param changelist Mode change list. Must remain valid and unchanged as long as this object is alive or until the next SetParams() call. + * @param beginiter Starting position of mode changes in 'changelist'. + */ + Mode(User* source, Channel* Chantarget, User* Usertarget, const Modes::ChangeList& changelist, Modes::ChangeList::List::const_iterator beginiter) + : ClientProtocol::Message("MODE", source) + , chantarget(Chantarget) + , usertarget(Usertarget) + , beginit(beginiter) + { + PushParamRef(GetStrTarget()); + PushParam(ToModeLetters(changelist.getlist(), 450, beginit, lastit)); + PushModeParams(); + } + + /** Constructor, populate parameters starting from the beginning of a mode change list. + * @param source User doing the mode change. + * @param Chantarget Channel target of the mode change. May be NULL if Usertarget is non-NULL. + * @param Usertarget User target of the mode change. May be NULL if Chantarget is non-NULL. + * @param changelist Mode change list. Must remain valid and unchanged as long as this object is alive or until the next SetParams() call. + */ + Mode(User* source, Channel* Chantarget, User* Usertarget, const Modes::ChangeList& changelist) + : ClientProtocol::Message("MODE", source) + , chantarget(Chantarget) + , usertarget(Usertarget) + , beginit(changelist.getlist().begin()) + { + PushParamRef(GetStrTarget()); + PushParam(ToModeLetters(changelist.getlist(), 450, beginit, lastit)); + PushModeParams(); + } + + /** Constructor. Does not populate parameters, call SetParams() before sending the message. + * The message source is set to the local server. + */ + Mode() + : ClientProtocol::Message("MODE", ServerInstance->FakeClient) + , chantarget(NULL) + , usertarget(NULL) + { + } + + /** Set parameters + * @param Chantarget Channel target of the mode change. May be NULL if Usertarget is non-NULL. + * @param Usertarget User target of the mode change. May be NULL if Chantarget is non-NULL. + * @param changelist Mode change list. Must remain valid and unchanged as long as this object is alive or until the next SetParams() call. + */ + void SetParams(Channel* Chantarget, User* Usertarget, const Modes::ChangeList& changelist) + { + ClearParams(); + + chantarget = Chantarget; + usertarget = Usertarget; + beginit = changelist.getlist().begin(); + + PushParamRef(GetStrTarget()); + PushParam(ToModeLetters(changelist.getlist(), 450, beginit, lastit)); + PushModeParams(); + } + + /** Get first mode change included in this MODE message. + * @return Iterator to the first mode change that is included in this MODE message. + */ + Modes::ChangeList::List::const_iterator GetBeginIterator() const { return beginit; } + + /** Get first mode change not included in this MODE message. + * @return Iterator to the first mode change that is not included in this MODE message. + */ + Modes::ChangeList::List::const_iterator GetEndIterator() const { return lastit; } + + /** Get mode change target as a string. + * This is the name of the channel if the mode change targets a channel or the nickname of the user + * if the target is a user. + * @return Name of target as a string. + */ + const std::string& GetStrTarget() const { return (chantarget ? chantarget->name : usertarget->nick); } + + /** Get user target. + * @return User target or NULL if the mode change targets a channel. + */ + User* GetUserTarget() const { return usertarget; } + + /** Get channel target. + * @return Channel target or NULL if the mode change targets a user. + */ + Channel* GetChanTarget() const { return chantarget; } +}; + +/** TOPIC message. + */ +struct ClientProtocol::Messages::Topic : public ClientProtocol::Message +{ + /** Constructor. + * @param source User changing the topic. + * @param chan Channel the topic is being changed on. + * @param newtopic New topic. May be empty, must remain valid as long as this object is alive. + */ + Topic(User* source, const Channel* chan, const std::string& newtopic) + : ClientProtocol::Message("TOPIC", source) + { + PushParamRef(chan->name); + PushParamRef(newtopic); + } +}; + +/** PRIVMSG and NOTICE message. + */ +class ClientProtocol::Messages::Privmsg : public ClientProtocol::Message +{ + void PushTargetChan(char status, const Channel* targetchan) + { + if (status) + { + std::string rawtarget(1, status); + rawtarget.append(targetchan->name); + PushParam(rawtarget); + } + else + { + PushParamRef(targetchan->name); + } + } + + void PushTargetUser(const User* targetuser) + { + if (targetuser->registered & REG_NICK) + PushParamRef(targetuser->nick); + else + PushParam("*"); + } + + public: + /** Used to differentiate constructors that copy the text from constructors that do not. + */ + enum NoCopy { nocopy }; + + /** Get command name from MessageType. + * @param mt Message type to get command name for. + * @return Command name for the message type. + */ + static const char* CommandStrFromMsgType(MessageType mt) + { + return ((mt == MSG_PRIVMSG) ? "PRIVMSG" : "NOTICE"); + } + + /** Constructor, user source, string target, copies text. + * @param source Source user. + * @param target Privmsg target string. + * @param text Privmsg text, will be copied. + * @param mt Message type. + */ + Privmsg(User* source, const std::string& target, const std::string& text, MessageType mt = MSG_PRIVMSG) + : ClientProtocol::Message(CommandStrFromMsgType(mt), source) + { + PushParam(target); + PushParam(text); + } + + /** Constructor, user source, user target, copies text. + * @param source Source user. + * @param targetchan Target channel. + * @param text Privmsg text, will be copied. + * @param mt Message type. + * @param status Prefix character for status messages. If non-zero the message is a status message. Optional, defaults to 0. + */ + Privmsg(User* source, const Channel* targetchan, const std::string& text, MessageType mt = MSG_PRIVMSG, char status = 0) + : ClientProtocol::Message(CommandStrFromMsgType(mt), source) + { + PushTargetChan(status, targetchan); + PushParam(text); + } + + /** Constructor, user source, user target, copies text. + * @param source Source user. + * @param targetuser Target user. + * @param text Privmsg text, will be copied. + * @param mt Message type. + */ + Privmsg(User* source, const User* targetuser, const std::string& text, MessageType mt = MSG_PRIVMSG) + : ClientProtocol::Message(CommandStrFromMsgType(mt), source) + { + PushTargetUser(targetuser); + PushParam(text); + } + + /** Constructor, string source, string target, copies text. + * @param source Source user. + * @param targetuser Target user. + * @param text Privmsg text, will be copied. + * @param mt Message type. + */ + Privmsg(const std::string& source, const std::string& target, const std::string& text, MessageType mt = MSG_PRIVMSG) + : ClientProtocol::Message(CommandStrFromMsgType(mt), source) + { + PushParam(target); + PushParam(text); + } + + /** Constructor, string source, channel target, copies text. + * @param source Source string. + * @param targetchan Target channel. + * @param text Privmsg text, will be copied. + * @param status Prefix character for status messages. If non-zero the message is a status message. Optional, defaults to 0. + * @param mt Message type. + */ + Privmsg(const std::string& source, const Channel* targetchan, const std::string& text, MessageType mt = MSG_PRIVMSG, char status = 0) + : ClientProtocol::Message(CommandStrFromMsgType(mt), source) + { + PushTargetChan(status, targetchan); + PushParam(text); + } + + /** Constructor, string source, user target, copies text. + * @param source Source string. + * @param targetuser Target user. + * @param text Privmsg text, will be copied. + * @param mt Message type. + */ + Privmsg(const std::string& source, const User* targetuser, const std::string& text, MessageType mt = MSG_PRIVMSG) + : ClientProtocol::Message(CommandStrFromMsgType(mt), source) + { + PushTargetUser(targetuser); + PushParam(text); + } + + /** Constructor, user source, string target, copies text. + * @param source Source user. + * @param targetuser Target string. + * @param text Privmsg text, will not be copied. + * @param mt Message type. + */ + Privmsg(NoCopy, User* source, const std::string& target, const std::string& text, MessageType mt = MSG_PRIVMSG) + : ClientProtocol::Message(CommandStrFromMsgType(mt), source) + { + PushParam(target); + PushParamRef(text); + } + + /** Constructor, user source, channel target, does not copy text. + * @param source Source user. + * @param targetchan Target channel. + * @param text Privmsg text, will not be copied. + * @param status Prefix character for status messages. If non-zero the message is a status message. Optional, defaults to 0. + * @param mt Message type. + */ + Privmsg(NoCopy, User* source, const Channel* targetchan, const std::string& text, MessageType mt = MSG_PRIVMSG, char status = 0) + : ClientProtocol::Message(CommandStrFromMsgType(mt), source) + { + PushTargetChan(status, targetchan); + PushParamRef(text); + } + + /** Constructor, user source, user target, does not copy text. + * @param source Source user. + * @param targetuser Target user. + * @param text Privmsg text, will not be copied. + * @param mt Message type. + */ + Privmsg(NoCopy, User* source, const User* targetuser, const std::string& text, MessageType mt = MSG_PRIVMSG) + : ClientProtocol::Message(CommandStrFromMsgType(mt), source) + { + PushTargetUser(targetuser); + PushParamRef(text); + } + + /** Constructor, string source, string target, does not copy text. + * @param source Source string. + * @param targetuser Target string. + * @param text Privmsg text, will not be copied. + * @param mt Message type. + */ + Privmsg(NoCopy, const std::string& source, const std::string& target, const std::string& text, MessageType mt = MSG_PRIVMSG) + : ClientProtocol::Message(CommandStrFromMsgType(mt), source) + { + PushParam(target); + PushParamRef(text); + } + + /** Constructor, string source, channel target, does not copy text. + * @param source Source string. + * @param targetchan Target channel. + * @param text Privmsg text, will not be copied. + * @param status Prefix character for status messages. If non-zero the message is a status message. Optional, defaults to 0. + * @param mt Message type. + */ + Privmsg(NoCopy, const std::string& source, const Channel* targetchan, const std::string& text, MessageType mt = MSG_PRIVMSG, char status = 0) + : ClientProtocol::Message(CommandStrFromMsgType(mt), source) + { + PushTargetChan(status, targetchan); + PushParamRef(text); + } + + /** Constructor, string source, user target, does not copy text. + * @param source Source string. + * @param targetchan Target user. + * @param text Privmsg text, will not be copied. + * @param mt Message type. + */ + Privmsg(NoCopy, const std::string& source, const User* targetuser, const std::string& text, MessageType mt = MSG_PRIVMSG) + : ClientProtocol::Message(CommandStrFromMsgType(mt), source) + { + PushTargetUser(targetuser); + PushParamRef(text); + } +}; + +/** INVITE message. + * Sent when a user is invited to join a channel. + */ +struct ClientProtocol::Messages::Invite : public ClientProtocol::Message +{ + /** Constructor. + * @param source User inviting the target user. + * @param target User being invited by source. + * @param chan Channel the target user is being invited to. + */ + Invite(User* source, User* target, Channel* chan) + : ClientProtocol::Message("INVITE", source) + { + PushParamRef(target->nick); + PushParamRef(chan->name); + } +}; + +/** PING message. + * Used to check if a connection is still alive. + */ +struct ClientProtocol::Messages::Ping : public ClientProtocol::Message +{ + /** Constructor. + * The ping cookie is the name of the local server. + */ + Ping() + : ClientProtocol::Message("PING") + { + PushParamRef(ServerInstance->Config->ServerName); + } + + /** Constructor. + * @param cookie Ping cookie. Must remain valid as long as this object is alive. + */ + Ping(const std::string& cookie) + : ClientProtocol::Message("PING") + { + PushParamRef(cookie); + } +}; + +/** PONG message. + * Sent as a reply to PING. + */ +struct ClientProtocol::Messages::Pong : public ClientProtocol::Message +{ + /** Constructor. + * @param cookie Ping cookie. Must remain valid as long as this object is alive. + */ + Pong(const std::string& cookie) + : ClientProtocol::Message("PONG", ServerInstance->Config->ServerName) + { + PushParamRef(ServerInstance->Config->ServerName); + PushParamRef(cookie); + } +}; + +/** ERROR message. + * Sent to clients upon disconnection. + */ +struct ClientProtocol::Messages::Error : public ClientProtocol::Message +{ + /** Constructor. + * @param text Error text. + */ + Error(const std::string& text) + : ClientProtocol::Message("ERROR") + { + PushParam(text); + } +}; diff --git a/include/command_parse.h b/include/command_parse.h index f9e3a740c..98484ca54 100644 --- a/include/command_parse.h +++ b/include/command_parse.h @@ -20,8 +20,7 @@ */ -#ifndef COMMAND_PARSE_H -#define COMMAND_PARSE_H +#pragma once /** This class handles command management and parsing. * It allows you to add and remove commands from the map, @@ -30,40 +29,44 @@ */ class CoreExport CommandParser { - private: - /** Process a parameter string into a list of items - * @param command_p The output list of items - * @param parameters The input string - * @return The number of parameters parsed into command_p - */ - int ProcessParameters(std::vector<std::string>& command_p, char* parameters); + public: + typedef TR1NS::unordered_map<std::string, Command*, irc::insensitive, irc::StrHashComp> CommandMap; + private: /** Process a command from a user. - * @param user The user to parse the command for - * @param cmd The command string to process + * @param user The user to parse the command for. + * @param command The name of the command. + * @param parameters The parameters to the command. */ - bool ProcessCommand(LocalUser *user, std::string &cmd); + void ProcessCommand(LocalUser* user, std::string& command, CommandBase::Params& parameters); - public: /** Command list, a hash_map of command names to Command* */ - Commandtable cmdlist; + CommandMap cmdlist; + public: /** Default constructor. */ CommandParser(); + /** Get a command name -> Command* map containing all client to server commands + * @return A map of command handlers keyed by command names + */ + const CommandMap& GetCommands() const { return cmdlist; } + /** Calls the handler for a given command. * @param commandname The command to find. This should be in uppercase. * @param parameters Parameter list * @param user The user to call the handler on behalf of + * @param cmd If non-NULL and the command was executed it is set to the command handler, + * otherwise it isn't written to. * @return This method will return CMD_SUCCESS if the command handler was found and called, * and the command completeld successfully. It will return CMD_FAILURE if the command handler was found * and called, but the command did not complete successfully, and it will return CMD_INVALID if the * command simply did not exist at all or the wrong number of parameters were given, or the user * was not privilaged enough to execute the command. */ - CmdResult CallHandler(const std::string &commandname, const std::vector<std::string>& parameters, User *user); + CmdResult CallHandler(const std::string& commandname, const CommandBase::Params& parameters, User* user, Command** cmd = NULL); /** Get the handler function for a command. * @param commandname The command required. Always use uppercase for this parameter. @@ -71,44 +74,50 @@ class CoreExport CommandParser */ Command* GetHandler(const std::string &commandname); - /** This function returns true if a command is valid with the given number of parameters and user. - * @param commandname The command name to check - * @param pcnt The parameter count - * @param user The user to check against - * @return If the user given has permission to execute the command, and the parameter count is - * equal to or greater than the minimum number of parameters to the given command, then this - * function will return true, otherwise it will return false. - */ - bool IsValidCommand(const std::string &commandname, unsigned int pcnt, User * user); - - /** LoopCall is used to call a command classes handler repeatedly based on the contents of a comma seperated list. - * There are two overriden versions of this method, one of which takes two potential lists and the other takes one. - * We need a version which takes two potential lists for JOIN, because a JOIN may contain two lists of items at once, + /** LoopCall is used to call a command handler repeatedly based on the contents of a comma seperated list. + * There are two ways to call this method, either with one potential list or with two potential lists. + * We need to handle two potential lists for JOIN, because a JOIN may contain two lists of items at once: * the channel names and their keys as follows: * * JOIN \#chan1,\#chan2,\#chan3 key1,,key3 * - * Therefore, we need to deal with both lists concurrently. The first instance of this method does that by creating - * two instances of irc::commasepstream and reading them both together until the first runs out of tokens. - * The second version is much simpler and just has the one stream to read, and is used in NAMES, WHOIS, PRIVMSG etc. - * Both will only parse until they reach ServerInstance->Config->MaxTargets number of targets, to stop abuse via spam. + * Therefore, we need to deal with both lists concurrently. If there are two lists then the method reads + * them both together until the first runs out of tokens. + * With one list it is much simpler, and is used in NAMES, WHOIS, PRIVMSG etc. + * + * If there is only one list and there are duplicates in it, then the command handler is only called for + * unique items. Entries are compared using "irc comparison". + * If the usemax parameter is true (the default) the function only parses until it reaches + * ServerInstance->Config->MaxTargets number of targets, to stop abuse via spam. + * + * The OnPostCommand hook is executed for each item after it has been processed by the handler, with the + * original line parameter being empty (to indicate that the command in that form was created by this function). + * This only applies if the user executing the command is local. + * + * If there are two lists and the second list runs out of tokens before the first list then parameters[extra] + * will be an EMPTY string when Handle() is called for the remaining tokens in the first list, even if it is + * in the middle of parameters[]! Moreover, empty tokens in the second list are allowed, and those will also + * result in the appropiate entry being empty in parameters[]. + * This is different than what command handlers usually expect; the command parser only allows an empty param + * as the last item in the vector. * * @param user The user who sent the command - * @param CommandObj the command object to call for each parameter in the list - * @param parameters Parameter list as an array of array of char (that's not a typo). + * @param handler The command handler to call for each parameter in the list + * @param parameters Parameter list as a vector of strings * @param splithere The first parameter index to split as a comma seperated list - * @param extra The second parameter index to split as a comma seperated list - * @param usemax Limit the command to MaxTargets targets - * @return This function will return 1 when there are no more parameters to process. When this occurs, its - * caller should return without doing anything, otherwise it should continue into its main section of code. + * @param extra The second parameter index to split as a comma seperated list, or -1 (the default) if there is only one list + * @param usemax True to limit the command to MaxTargets targets (default), or false to process all tokens + * @return This function returns true when it identified a list in the given parameter and finished calling the + * command handler for each entry on the list. When this occurs, the caller should return without doing anything, + * otherwise it should continue into its main section of code. */ - int LoopCall(User* user, Command* CommandObj, const std::vector<std::string>& parameters, unsigned int splithere, int extra = -1, bool usemax = true); + static bool LoopCall(User* user, Command* handler, const CommandBase::Params& parameters, unsigned int splithere, int extra = -1, bool usemax = true); /** Take a raw input buffer from a recvq, and process it on behalf of a user. * @param buffer The buffer line to process * @param user The user to whom this line belongs */ - bool ProcessBuffer(std::string &buffer,LocalUser *user); + void ProcessBuffer(LocalUser* user, const std::string& buffer); /** Add a new command to the commands hash * @param f The new Command to add to the list @@ -120,23 +129,23 @@ class CoreExport CommandParser */ void RemoveCommand(Command* x); - /** Translate nicknames in a string into UIDs, based on the TranslationType given. - * @param to The translation type to use for the process. - * @param source The input string - * @param dest The output string, it is safe to pass source and dest as the same variable only for translation type TR_TEXT. - * @return returns the number of substitutions made. Will always be 0 or 1 + /** Translate a single item based on the TranslationType given. + * @param to The translation type to use for the process + * @param item The input string + * @param dest The output string. The translation result will be appended to this string + * @param custom_translator Used to translate the parameter if the translation type is TR_CUSTOM, if NULL, TR_CUSTOM will act like TR_TEXT + * @param paramnumber The index of the parameter we are translating. */ - int TranslateUIDs(TranslateType to, const std::string &source, std::string &dest); + static void TranslateSingleParam(TranslateType to, const std::string& item, std::string& dest, CommandBase* custom_translator = NULL, unsigned int paramnumber = 0); /** Translate nicknames in a list of strings into UIDs, based on the TranslateTypes given. * @param to The translation types to use for the process. If this list is too short, TR_TEXT is assumed for the rest. * @param source The strings to translate - * @param dest The output string * @param prefix_final True if the final source argument should have a colon prepended (if it could contain a space) - * @param custom_translator Used to translate the parameter if the TR_CUSTOM type is found in to - * @return returns the number of substitutions made. + * @param custom_translator Used to translate the parameter if the translation type is TR_CUSTOM, if NULL, TR_CUSTOM will act like TR_TEXT + * @return dest The output string */ - int TranslateUIDs(const std::vector<TranslateType> to, const std::vector<std::string> &source, std::string &dest, bool prefix_final = false, Command* custom_translator = NULL); + static std::string TranslateUIDs(const std::vector<TranslateType>& to, const CommandBase::Params& source, bool prefix_final = false, CommandBase* custom_translator = NULL); }; /** A lookup table of values for multiplier characters used by @@ -165,5 +174,3 @@ const int duration_multi[] = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; - -#endif diff --git a/include/commands/cmd_whowas.h b/include/commands/cmd_whowas.h index d33354122..2447024a1 100644 --- a/include/commands/cmd_whowas.h +++ b/include/commands/cmd_whowas.h @@ -19,50 +19,171 @@ */ -#ifndef CMD_WHOWAS_H -#define CMD_WHOWAS_H +#pragma once + #include "modules.h" -struct WhowasRequest : public Request +namespace WhoWas { - /* list of available internal commands */ - enum Internals + /** One entry for a nick. There may be multiple entries for a nick. + */ + struct Entry { - WHOWAS_ADD = 1, - WHOWAS_STATS = 2, - WHOWAS_PRUNE = 3, - WHOWAS_MAINTAIN = 4 - }; + /** Real host + */ + const std::string host; - const Internals type; - std::string value; - User* user; + /** Displayed host + */ + const std::string dhost; - WhowasRequest(Module* src, Module* whowas, Internals Type) : Request(src, whowas, "WHOWAS"), type(Type) - {} -}; + /** Ident + */ + const std::string ident; -/* Forward ref for timer */ -class WhoWasMaintainTimer; + /** Server name + */ + const std::string server; -/* Forward ref for typedefs */ -class WhoWasGroup; + /** Real name + */ + const std::string real; -/** Timer that is used to maintain the whowas list, called once an hour - */ -extern WhoWasMaintainTimer* timer; + /** Signon time + */ + const time_t signon; -/** A group of users related by nickname - */ -typedef std::deque<WhoWasGroup*> whowas_set; + /** Initialize this Entry with a user + */ + Entry(User* user); + }; -/** Sets of users in the whowas system - */ -typedef std::map<irc::string,whowas_set*> whowas_users; + /** Everything known about one nick + */ + struct Nick : public insp::intrusive_list_node<Nick> + { + /** A group of users related by nickname + */ + typedef std::deque<Entry*> List; -/** Sets of time and users in whowas list - */ -typedef std::deque<std::pair<time_t,irc::string> > whowas_users_fifo; + /** Container where each element has information about one occurrence of this nick + */ + List entries; + + /** Time this nick was added to the database + */ + const time_t addtime; + + /** Nickname whose information is stored in this class + */ + const std::string nick; + + /** Constructor to initialize fields + */ + Nick(const std::string& nickname); + + /** Destructor, deallocates all elements in the entries container + */ + ~Nick(); + }; + + class Manager + { + public: + struct Stats + { + /** Number of currently existing WhoWas::Entry objects + */ + size_t entrycount; + }; + + /** Add a user to the whowas database. Called when a user quits. + * @param user The user to add to the database + */ + void Add(User* user); + + /** Retrieves statistics about the whowas database + * @return Whowas statistics as a WhoWas::Manager::Stats struct + */ + Stats GetStats() const; + + /** Expires old entries + */ + void Maintain(); + + /** Updates the current configuration which may result in the database being pruned if the + * new values are lower than the current ones. + * @param NewGroupSize Maximum number of nicks allowed in the database. In case there are this many nicks + * in the database and one more is added, the oldest one is removed (FIFO). + * @param NewMaxGroups Maximum number of entries per nick + * @param NewMaxKeep Seconds how long each nick should be kept + */ + void UpdateConfig(unsigned int NewGroupSize, unsigned int NewMaxGroups, unsigned int NewMaxKeep); + + /** Retrieves all data known about a given nick + * @param nick Nickname to find, case insensitive (IRC casemapping) + * @return A pointer to a WhoWas::Nick if the nick was found, NULL otherwise + */ + const Nick* FindNick(const std::string& nick) const; + + /** Returns true if WHOWAS is enabled according to the current configuration + * @return True if WHOWAS is enabled according to the configuration, false if WHOWAS is disabled + */ + bool IsEnabled() const; + + /** Constructor + */ + Manager(); + + /** Destructor + */ + ~Manager(); + + private: + /** Order in which the users were added into the map, used to remove oldest nick + */ + typedef insp::intrusive_list_tail<Nick> FIFO; + + /** Sets of users in the whowas system + */ + typedef TR1NS::unordered_map<std::string, WhoWas::Nick*, irc::insensitive, irc::StrHashComp> whowas_users; + + /** Primary container, links nicknames tracked by WHOWAS to a list of records + */ + whowas_users whowas; + + /** List of nicknames in the order they were inserted into the map + */ + FIFO whowas_fifo; + + /** Max number of WhoWas entries per user. + */ + unsigned int GroupSize; + + /** Max number of cumulative user-entries in WhoWas. + * When max reached and added to, push out oldest entry FIFO style. + */ + unsigned int MaxGroups; + + /** Max seconds a user is kept in WhoWas before being pruned. + */ + unsigned int MaxKeep; + + /** Shrink all data structures to honor the current settings + */ + void Prune(); + + /** Remove a nick (and all entries belonging to it) from the database + * @param it Iterator to the nick to purge + */ + void PurgeNick(whowas_users::iterator it); + + /** Remove a nick (and all entries belonging to it) from the database + * @param nick Nick to purge + */ + void PurgeNick(WhoWas::Nick* nick); + }; +} /** Handle /WHOWAS. These command handlers can be reloaded by the core, * and handle basic RFC1459 commands. Commands within modules work @@ -71,71 +192,16 @@ typedef std::deque<std::pair<time_t,irc::string> > whowas_users_fifo; */ class CommandWhowas : public Command { - private: - /** Whowas container, contains a map of vectors of users tracked by WHOWAS - */ - whowas_users whowas; - - /** Whowas container, contains a map of time_t to users tracked by WHOWAS + public: + /** Manager handling all whowas database related tasks */ - whowas_users_fifo whowas_fifo; + WhoWas::Manager manager; - public: CommandWhowas(Module* parent); /** 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); - void AddToWhoWas(User* user); - std::string GetStats(); - void PruneWhoWas(time_t t); - void MaintainWhoWas(time_t t); - ~CommandWhowas(); + CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE; }; - -/** Used to hold WHOWAS information - */ -class WhoWasGroup -{ - public: - /** Real host - */ - std::string host; - /** Displayed host - */ - std::string dhost; - /** Ident - */ - std::string ident; - /** Server name - */ - std::string server; - /** Fullname (GECOS) - */ - std::string gecos; - /** Signon time - */ - time_t signon; - - /** Initialize this WhoWasFroup with a user - */ - WhoWasGroup(User* user); - /** Destructor - */ - ~WhoWasGroup(); -}; - -class WhoWasMaintainTimer : public Timer -{ - public: - WhoWasMaintainTimer(long interval) - : Timer(interval, ServerInstance->Time(), true) - { - } - virtual void Tick(time_t TIME); -}; - -#endif diff --git a/include/compat.h b/include/compat.h new file mode 100644 index 000000000..7a95e9050 --- /dev/null +++ b/include/compat.h @@ -0,0 +1,104 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2013 Peter Powell <petpow@saberuk.com> + * Copyright (C) 2008 Thomas Stagner <aquanight@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/>. + */ + + +#pragma once + +/** + * Some implementations of the C++11 standard library are incomplete so we use + * the implementation of the same types from C++ Technical Report 1 instead. + */ +#if defined _LIBCPP_VERSION || defined _WIN32 +# define TR1NS std +# include <array> +# include <functional> +# include <unordered_map> +# include <type_traits> +#else +# define TR1NS std::tr1 +# include <tr1/array> +# include <tr1/functional> +# include <tr1/unordered_map> +# include <tr1/type_traits> +#endif + +/** + * This macro enables the compile-time checking of printf format strings. This + * makes the compiler show a warning if the format of a printf arguments are + * incorrect. + */ +#if defined __clang__ || defined __GNUC__ +# define CUSTOM_PRINTF(stringpos, firstpos) __attribute__((format(printf, stringpos, firstpos))) +#else +# define CUSTOM_PRINTF(stringpos, firstpos) +#endif + +/** + * These macros enable the use of the C++11 override control keywords in + * compilers which support them. + */ +#if __cplusplus >= 201103L +# define HAS_CXX11_FINAL_OVERRIDE +#elif defined __clang__ +# if __has_feature(cxx_override_control) +# define HAS_CXX11_FINAL_OVERRIDE +# endif +#elif (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) +# if defined __GXX_EXPERIMENTAL_CXX0X__ +# define HAS_CXX11_FINAL_OVERRIDE +# endif +#elif _MSC_VER >= 1700 +# define HAS_CXX11_FINAL_OVERRIDE +#endif + +#if defined HAS_CXX11_FINAL_OVERRIDE +# define CXX11_FINAL final +# define CXX11_OVERRIDE override +#else +# define CXX11_FINAL +# define CXX11_OVERRIDE +#endif + +/** + * This macro allows methods to be marked as deprecated. To use this, wrap the + * method declaration in the header file with the macro. + */ +#if defined __clang__ || defined __GNUC__ +# define DEPRECATED_METHOD(function) function __attribute__((deprecated)) +#elif defined _MSC_VER +# define DEPRECATED_METHOD(function) __declspec(deprecated) function +#else +# define DEPRECATED_METHOD(function) function +#endif + +/** + * Windows is very different to UNIX so we have to wrap certain features in + * order to build on Windows correctly. + */ +#if defined _WIN32 +# include "inspircd_win32wrapper.h" +# include "threadengines/threadengine_win32.h" +#else +# define ENTRYPOINT int main(int argc, char** argv) +# define DllExport __attribute__ ((visibility ("default"))) +# define CoreExport __attribute__ ((visibility ("default"))) +# include <unistd.h> +# include "threadengines/threadengine_pthread.h" +#endif diff --git a/include/configparser.h b/include/configparser.h index 999d79e24..c9790c59f 100644 --- a/include/configparser.h +++ b/include/configparser.h @@ -17,6 +17,8 @@ */ +#pragma once + struct fpos { std::string filename; @@ -31,7 +33,7 @@ struct fpos enum ParseFlags { - FLAG_USE_XML = 1, + FLAG_USE_COMPAT = 1, FLAG_NO_EXEC = 2, FLAG_NO_INC = 4 }; @@ -39,7 +41,7 @@ enum ParseFlags struct ParseStack { std::vector<std::string> reading; - std::map<std::string, std::string> vars; + insp::flat_map<std::string, std::string, irc::insensitive_swo> vars; ConfigDataHash& output; ConfigFileCache& FilesOutput; std::stringstream& errstr; @@ -51,8 +53,7 @@ struct ParseStack vars["quot"] = "\""; vars["newline"] = vars["nl"] = "\n"; } - bool ParseFile(const std::string& name, int flags, const std::string& mandatory_tag = ""); - bool ParseExec(const std::string& name, int flags, const std::string& mandatory_tag = ""); + bool ParseFile(const std::string& name, int flags, const std::string& mandatory_tag = std::string(), bool isexec = false); void DoInclude(ConfigTag* includeTag, int flags); void DoReadFile(const std::string& key, const std::string& file, int flags, bool exec); }; @@ -76,5 +77,3 @@ struct FileWrapper } } }; - - diff --git a/include/configreader.h b/include/configreader.h index 4a697d05c..4cb051eff 100644 --- a/include/configreader.h +++ b/include/configreader.h @@ -21,8 +21,7 @@ */ -#ifndef INSPIRCD_CONFIGREADER -#define INSPIRCD_CONFIGREADER +#pragma once #include <sstream> #include <string> @@ -32,25 +31,40 @@ #include "modules.h" #include "socketengine.h" #include "socket.h" +#include "token_list.h" /** Structure representing a single \<tag> in config */ class CoreExport ConfigTag : public refcountbase { - std::vector<KeyVal> items; + ConfigItems items; public: const std::string tag; const std::string src_name; const int src_line; /** Get the value of an option, using def if it does not exist */ - std::string getString(const std::string& key, const std::string& def = ""); + std::string getString(const std::string& key, const std::string& def, const TR1NS::function<bool(const std::string&)>& validator); /** Get the value of an option, using def if it does not exist */ - long getInt(const std::string& key, long def = 0); + std::string getString(const std::string& key, const std::string& def = "", size_t minlen = 0, size_t maxlen = UINT32_MAX); /** Get the value of an option, using def if it does not exist */ - double getFloat(const std::string& key, double def = 0); + long getInt(const std::string& key, long def, long min = LONG_MIN, long max = LONG_MAX); + /** Get the value of an option, using def if it does not exist */ + unsigned long getUInt(const std::string& key, unsigned long def, unsigned long min = 0, unsigned long max = ULONG_MAX); + /** Get the value of an option, using def if it does not exist */ + double getFloat(const std::string& key, double def, double min = DBL_MIN, double max = DBL_MAX); /** Get the value of an option, using def if it does not exist */ bool getBool(const std::string& key, bool def = false); + /** Get the value in seconds of a duration that is in the user-friendly "1h2m3s" format, + * using a default value if it does not exist or is out of bounds. + * @param key The config key name + * @param def Default value (optional) + * @param min Minimum acceptable value (optional) + * @param max Maximum acceptable value (optional) + * @return The duration in seconds + */ + unsigned long getDuration(const std::string& key, unsigned long def, unsigned long min = 0, unsigned long max = ULONG_MAX); + /** Get the value of an option * @param key The option to get * @param value The location to store the value (unmodified if does not exist) @@ -61,10 +75,10 @@ class CoreExport ConfigTag : public refcountbase std::string getTagLocation(); - inline const std::vector<KeyVal>& getItems() const { return items; } + inline const ConfigItems& getItems() const { return items; } - /** Create a new ConfigTag, giving access to the private KeyVal item list */ - static ConfigTag* create(const std::string& Tag, const std::string& file, int line, std::vector<KeyVal>*& Items); + /** Create a new ConfigTag, giving access to the private ConfigItems item list */ + static ConfigTag* create(const std::string& Tag, const std::string& file, int line, ConfigItems*& Items); private: ConfigTag(const std::string& Tag, const std::string& file, int line); }; @@ -89,18 +103,22 @@ class ServerLimits size_t MaxTopic; /** Maximum kick message length */ size_t MaxKick; - /** Maximum GECOS (real name) length */ - size_t MaxGecos; + /** Maximum real name length */ + size_t MaxReal; /** Maximum away message length */ size_t MaxAway; + /** Maximum line length */ + size_t MaxLine; + /** Maximum hostname length */ + size_t MaxHost; - /** Creating the class initialises it to the defaults - * as in 1.1's ./configure script. Reading other values - * from the config will change these values. + /** Read all limits from a config tag. Limits which aren't specified in the tag are set to a default value. + * @param tag Configuration tag to read the limits from */ - ServerLimits() : NickMax(31), ChanMax(64), MaxModes(20), IdentMax(12), MaxQuit(255), MaxTopic(307), MaxKick(255), MaxGecos(128), MaxAway(200) - { - } + ServerLimits(ConfigTag* tag); + + /** Maximum length of a n!u\@h mask */ + size_t GetMaxMask() const { return NickMax + 1 + IdentMax + 1 + MaxHost; } }; struct CommandLineConf @@ -130,10 +148,17 @@ struct CommandLineConf */ bool writelog; - /** True if we have been told to run the testsuite from the commandline, - * rather than entering the mainloop. + /** If this is true, a PID file will be written + * to the file given in the "file" variable of + * the <pid> tag in the config file. This is + * the default. + * Passing --nopid as a command line argument + * sets this to false; in this case, a PID file + * will not be written, even the default PID + * file that is usually written when the <pid> + * tag is not defined in the config file. */ - bool TestSuite; + bool writepid; /** Saved argc from startup */ @@ -142,15 +167,13 @@ struct CommandLineConf /** Saved argv from startup */ char** argv; - - std::string startup_log; }; class CoreExport OperInfo : public refcountbase { public: - std::set<std::string> AllowedOperCommands; - std::set<std::string> AllowedPrivs; + TokenList AllowedOperCommands; + TokenList AllowedPrivs; /** Allowed user modes from oper classes. */ std::bitset<64> AllowedUserModes; @@ -167,14 +190,14 @@ class CoreExport OperInfo : public refcountbase /** Name of the oper type; i.e. the one shown in WHOIS */ std::string name; + /** Creates a new OperInfo with the specified oper type name. + * @param Name The name of the oper type. + */ + OperInfo(const std::string& Name); + /** Get a configuration item, searching in the oper, type, and class blocks (in that order) */ std::string getConfig(const std::string& key); void init(); - - inline const char* NameStr() - { - return irc::Spacify(name.c_str()); - } }; /** This class holds the bulk of the runtime configuration for the ircd. @@ -189,6 +212,49 @@ class CoreExport ServerConfig void CrossCheckConnectBlocks(ServerConfig* current); public: + /** How to treat a user in a channel who is banned. */ + enum BannedUserTreatment + { + /** Don't treat a banned user any different to normal. */ + BUT_NORMAL, + + /** Restrict the actions of a banned user. */ + BUT_RESTRICT_SILENT, + + /** Restrict the actions of a banned user and notify them of their treatment. */ + BUT_RESTRICT_NOTIFY + }; + + class ServerPaths + { + public: + /** Config path */ + std::string Config; + + /** Data path */ + std::string Data; + + /** Log path */ + std::string Log; + + /** Module path */ + std::string Module; + + ServerPaths(ConfigTag* tag); + + std::string PrependConfig(const std::string& fn) const { return FileSystem::ExpandPath(Config, fn); } + std::string PrependData(const std::string& fn) const { return FileSystem::ExpandPath(Data, fn); } + std::string PrependLog(const std::string& fn) const { return FileSystem::ExpandPath(Log, fn); } + std::string PrependModule(const std::string& fn) const { return FileSystem::ExpandPath(Module, fn); } + }; + + /** Holds a complete list of all connect blocks + */ + typedef std::vector<reference<ConnectClass> > ClassVector; + + /** Index of valid oper blocks and types + */ + typedef insp::flat_map<std::string, reference<OperInfo> > OperIndex; /** Get a configuration tag * @param tag The name of the tag to get @@ -210,11 +276,7 @@ class CoreExport ServerConfig /** Bind to IPv6 by default */ bool WildcardIPv6; - /** Used to indicate who we announce invites to on a channel */ - enum InviteAnnounceState { INVITE_ANNOUNCE_NONE, INVITE_ANNOUNCE_ALL, INVITE_ANNOUNCE_OPS, INVITE_ANNOUNCE_DYNAMIC }; - enum OperSpyWhoisState { SPYWHOIS_NONE, SPYWHOIS_SINGLEMSG, SPYWHOIS_SPLITMSG }; - - /** This holds all the information in the config file, + /** This holds all the information in the config file, * it's indexed by tag name to a vector of key/values. */ ConfigDataHash config_data; @@ -228,6 +290,9 @@ class CoreExport ServerConfig */ ServerLimits Limits; + /** Locations of various types of file (config, module, etc). */ + ServerPaths Paths; + /** Configuration parsed from the command line. */ CommandLineConf cmdline; @@ -235,34 +300,21 @@ class CoreExport ServerConfig /** Clones CIDR range for ipv4 (0-32) * Defaults to 32 (checks clones on all IPs seperately) */ - int c_ipv4_range; + unsigned char c_ipv4_range; /** Clones CIDR range for ipv6 (0-128) * Defaults to 128 (checks on all IPs seperately) */ - int c_ipv6_range; - - /** Max number of WhoWas entries per user. - */ - int WhoWasGroupSize; - - /** Max number of cumulative user-entries in WhoWas. - * When max reached and added to, push out oldest entry FIFO style. - */ - int WhoWasMaxGroups; - - /** Max seconds a user is kept in WhoWas before being pruned. - */ - int WhoWasMaxKeep; + unsigned char c_ipv6_range; /** Holds the server name of the local server * as defined by the administrator. */ std::string ServerName; - /** Notice to give to users when they are Xlined + /** Notice to give to users when they are banned by an XLine */ - std::string MoronBanner; + std::string XLineMessage; /* Holds the network name the local server * belongs to. This is an arbitary field defined @@ -275,95 +327,17 @@ class CoreExport ServerConfig */ std::string ServerDesc; - /** Holds the admin's name, for output in - * the /ADMIN command. - */ - std::string AdminName; - - /** Holds the email address of the admin, - * for output in the /ADMIN command. - */ - std::string AdminEmail; - - /** Holds the admin's nickname, for output - * in the /ADMIN command - */ - std::string AdminNick; - - /** The admin-configured /DIE password - */ - std::string diepass; - - /** The admin-configured /RESTART password - */ - std::string restartpass; - - /** The hash method for *BOTH* the die and restart passwords. - */ - std::string powerhash; - - /** The pathname and filename of the message of the - * day file, as defined by the administrator. - */ - std::string motd; - - /** The pathname and filename of the rules file, - * as defined by the administrator. - */ - std::string rules; - - /** The quit prefix in use, or an empty string - */ - std::string PrefixQuit; - - /** The quit suffix in use, or an empty string - */ - std::string SuffixQuit; - - /** The fixed quit message in use, or an empty string - */ - std::string FixedQuit; - - /** The part prefix in use, or an empty string - */ - std::string PrefixPart; - - /** The part suffix in use, or an empty string - */ - std::string SuffixPart; - - /** The fixed part message in use, or an empty string - */ - std::string FixedPart; - - /** The DNS server to use for DNS queries - */ - std::string DNSServer; - /** Pretend disabled commands don't exist. */ bool DisabledDontExist; - /** This variable contains a space-seperated list - * of commands which are disabled by the - * administrator of the server for non-opers. - */ - std::string DisabledCommands; - /** This variable identifies which usermodes have been diabled. */ - char DisabledUModes[64]; + std::bitset<64> DisabledUModes; /** This variable identifies which chanmodes have been disabled. */ - char DisabledCModes[64]; - - /** The full path to the modules directory. - * This is either set at compile time, or - * overridden in the configuration file via - * the \<path> tag. - */ - std::string ModPath; + std::bitset<64> DisabledCModes; /** If set to true, then all opers on this server are * shown with a generic 'is an IRC operator' line rather @@ -371,21 +345,8 @@ class CoreExport ServerConfig */ bool GenericOper; - /** If this value is true, banned users (+b, not extbans) will not be able to change nick - * if banned on any channel, nor to message them. - */ - bool RestrictBannedUsers; - - /** If this is set to true, then mode lists (e.g - * MODE \#chan b) are hidden from unprivileged - * users. - */ - bool HideModeLists[256]; - - /** The number of seconds the DNS subsystem - * will wait before timing out any request. - */ - int dns_timeout; + /** How to treat a user in a channel who is banned. */ + BannedUserTreatment RestrictBannedUsers; /** The size of the read() buffer in the user * handling code, used to read data into a user's @@ -398,6 +359,13 @@ class CoreExport ServerConfig */ int MaxConn; + /** If we should check for clones during CheckClass() in AddUser() + * Setting this to false allows to not trigger on maxclones for users + * that may belong to another class after DNS-lookup is complete. + * It does, however, make the server spend more time on users we may potentially not want. + */ + bool CCOnConnect; + /** The soft limit value assigned to the irc server. * The IRC server will not allow more than this * number of local users. @@ -418,21 +386,11 @@ class CoreExport ServerConfig */ bool HideBans; - /** Announce invites to the channel with a server notice - */ - InviteAnnounceState AnnounceInvites; - - /** If this is enabled then operators will - * see invisible (+i) channels in /whois. - */ - OperSpyWhoisState OperSpyWhois; - /** True if raw I/O is being logged */ bool RawLog; - /** Set to a non-empty string to obfuscate the server name of users in WHOIS - */ - std::string HideWhoisServer; + /** Set to a non-empty string to obfuscate server names. */ + std::string HideServer; /** Set to a non empty string to obfuscate nicknames prepended to a KILL. */ @@ -451,16 +409,6 @@ class CoreExport ServerConfig */ ClassVector Classes; - /** The 005 tokens of this server (ISUPPORT) - * populated/repopulated upon loading or unloading - * modules. - */ - std::string data005; - - /** isupport strings - */ - std::vector<std::string> isupport; - /** STATS characters in this list are available * only to operators. */ @@ -474,52 +422,32 @@ class CoreExport ServerConfig */ std::string CustomVersion; - /** List of u-lined servers - */ - std::map<irc::string, bool> ulines; - - /** Max banlist sizes for channels (the std::string is a glob) - */ - std::map<std::string, int> maxbans; - - /** If set to true, no user DNS lookups are to be performed - */ - bool NoUserDns; - /** If set to true, provide syntax hints for unknown commands */ bool SyntaxHints; - /** If set to true, users appear to quit then rejoin when their hosts change. - * This keeps clients synchronized properly. - */ - bool CycleHosts; - - /** If set to true, the CycleHosts mode change will be sourced from the user, - * rather than the server + /** The name of the casemapping method used by this server. */ - bool CycleHostsFromUser; - - /** If set to true, prefixed channel NOTICEs and PRIVMSGs will have the prefix - * added to the outgoing text for undernet style msg prefixing. - */ - bool UndernetMsgPrefix; + std::string CaseMapping; /** If set to true, the full nick!user\@host will be shown in the TOPIC command * for who set the topic last. If false, only the nick is shown. */ bool FullHostInTopic; - /** Oper block and type index. - * For anonymous oper blocks (type only), prefix with a space. + /** Oper blocks keyed by their name */ OperIndex oper_blocks; - /** Max channels per user + /** Oper types keyed by their name + */ + OperIndex OperTypes; + + /** Default value for <connect:maxchans>, deprecated in 3.0 */ unsigned int MaxChans; - /** Oper max channels per user + /** Default value for <oper:maxchans>, deprecated in 3.0 */ unsigned int OperMaxChans; @@ -538,15 +466,7 @@ class CoreExport ServerConfig /** Get server ID as string with required leading zeroes */ - const std::string& GetSID(); - - /** Update the 005 vector - */ - void Update005(); - - /** Send the 005 numerics (ISUPPORT) to a user - */ - void Send005(User* user); + const std::string& GetSID() const { return sid; } /** Read the entire configuration into memory * and initialize this class. All other methods @@ -561,36 +481,18 @@ class CoreExport ServerConfig void Fill(); - /** Returns true if the given string starts with a windows drive letter - */ - bool StartsWithWindowsDriveLetter(const std::string &path); - - bool ApplyDisabledCommands(const std::string& data); - - /** Clean a filename, stripping the directories (and drives) from string. - * @param name Directory to tidy - * @return The cleaned filename - */ - static const char* CleanFilename(const char* name); + /** Disables the commands specified in <disabled:commands>. */ + bool ApplyDisabledCommands(); - /** Check if a file exists. - * @param file The full path to a file - * @return True if the file exists and is readable. + /** Escapes a value for storage in a configuration key. + * @param str The string to escape. + * @param xml Are we using the XML config format? */ - static bool FileExists(const char* file); - - /** If this value is true, invites will bypass more than just +i - */ - bool InvBypassModes; + static std::string Escape(const std::string& str, bool xml = true); /** If this value is true, snotices will not stack when repeats are sent */ bool NoSnoticeStack; - - /** If true, a "Welcome to <networkname>!" NOTICE will be sent to - * connecting users - */ - bool WelcomeNotice; }; /** The background thread for config reading, so that reading from executable includes @@ -612,10 +514,19 @@ class CoreExport ConfigReaderThread : public Thread delete Config; } - void Run(); + void Run() CXX11_OVERRIDE; /** Run in the main thread to apply the configuration */ void Finish(); bool IsDone() { return done; } }; -#endif +class CoreExport ConfigStatus +{ + public: + User* const srcuser; + + ConfigStatus(User* user = NULL) + : srcuser(user) + { + } +}; diff --git a/include/consolecolors.h b/include/consolecolors.h index f7ca1335e..d92be58bc 100644 --- a/include/consolecolors.h +++ b/include/consolecolors.h @@ -14,8 +14,8 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef CONSOLECOLORS_H -#define CONSOLECOLORS_H + +#pragma once #include <ostream> @@ -29,72 +29,70 @@ extern HANDLE g_hStdout; inline std::ostream& con_green(std::ostream &s) { - SetConsoleTextAttribute(g_hStdout, FOREGROUND_GREEN|FOREGROUND_INTENSITY|g_wBackgroundColor); - return s; + SetConsoleTextAttribute(g_hStdout, FOREGROUND_GREEN|FOREGROUND_INTENSITY|g_wBackgroundColor); + return s; } inline std::ostream& con_red(std::ostream &s) { - SetConsoleTextAttribute(g_hStdout, FOREGROUND_RED|FOREGROUND_INTENSITY|g_wBackgroundColor); - return s; + SetConsoleTextAttribute(g_hStdout, FOREGROUND_RED|FOREGROUND_INTENSITY|g_wBackgroundColor); + return s; } inline std::ostream& con_white(std::ostream &s) { - SetConsoleTextAttribute(g_hStdout, FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_GREEN|g_wBackgroundColor); - return s; + SetConsoleTextAttribute(g_hStdout, FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_GREEN|g_wBackgroundColor); + return s; } inline std::ostream& con_white_bright(std::ostream &s) { - SetConsoleTextAttribute(g_hStdout, FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_INTENSITY|g_wBackgroundColor); - return s; + SetConsoleTextAttribute(g_hStdout, FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_INTENSITY|g_wBackgroundColor); + return s; } inline std::ostream& con_bright(std::ostream &s) { - SetConsoleTextAttribute(g_hStdout, FOREGROUND_INTENSITY|g_wBackgroundColor); - return s; + SetConsoleTextAttribute(g_hStdout, FOREGROUND_INTENSITY|g_wBackgroundColor); + return s; } inline std::ostream& con_reset(std::ostream &s) { - SetConsoleTextAttribute(g_hStdout, g_wOriginalColors); - return s; + SetConsoleTextAttribute(g_hStdout, g_wOriginalColors); + return s; } #else inline std::ostream& con_green(std::ostream &s) { - return s << "\033[1;32m"; + return s << "\033[1;32m"; } inline std::ostream& con_red(std::ostream &s) { - return s << "\033[1;31m"; + return s << "\033[1;31m"; } inline std::ostream& con_white(std::ostream &s) { - return s << "\033[0m"; + return s << "\033[0m"; } inline std::ostream& con_white_bright(std::ostream &s) { - return s << "\033[1m"; + return s << "\033[1m"; } inline std::ostream& con_bright(std::ostream &s) { - return s << "\033[1m"; + return s << "\033[1m"; } inline std::ostream& con_reset(std::ostream &s) { - return s << "\033[0m"; + return s << "\033[0m"; } #endif - -#endif diff --git a/include/convto.h b/include/convto.h new file mode 100644 index 000000000..c306283fc --- /dev/null +++ b/include/convto.h @@ -0,0 +1,110 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com> + * Copyright (C) 2006 Craig Edwards <craigedwards@brainbox.cc> + * + * 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/>. + */ + + +#pragma once + +/** Template function to convert any input type to std::string + */ +template<typename T> inline std::string ConvNumeric(const T& in) +{ + if (in == 0) + return "0"; + T quotient = in; + std::string out; + while (quotient) + { + out += "0123456789"[std::abs((long)quotient % 10)]; + quotient /= 10; + } + if (in < 0) + out += '-'; + std::reverse(out.begin(), out.end()); + return out; +} + +/** Template function to convert any input type to std::string + */ +inline std::string ConvToStr(const int in) +{ + return ConvNumeric(in); +} + +/** Template function to convert any input type to std::string + */ +inline std::string ConvToStr(const long in) +{ + return ConvNumeric(in); +} + +/** Template function to convert any input type to std::string + */ +inline std::string ConvToStr(const char* in) +{ + return in; +} + +/** Template function to convert any input type to std::string + */ +inline std::string ConvToStr(const bool in) +{ + return (in ? "1" : "0"); +} + +/** Template function to convert any input type to std::string + */ +inline std::string ConvToStr(char in) +{ + return std::string(1, in); +} + +inline const std::string& ConvToStr(const std::string& in) +{ + return in; +} + +/** Template function to convert any input type to std::string + */ +template <class T> inline std::string ConvToStr(const T& in) +{ + std::stringstream tmp; + if (!(tmp << in)) + return std::string(); + return tmp.str(); +} + +/** Template function to convert any input type to any other type + * (usually an integer or numeric type) + */ +template<typename T> inline long ConvToInt(const T& in) +{ + std::stringstream tmp; + if (!(tmp << in)) + return 0; + return atol(tmp.str().c_str()); +} + +template<typename TOut> inline TOut ConvToNum(const std::string& in) +{ + TOut ret; + std::istringstream tmp(in); + if (!(tmp >> ret)) + return 0; + return ret; +} diff --git a/include/ctables.h b/include/ctables.h index f9cd08cb3..8be40cc54 100644 --- a/include/ctables.h +++ b/include/ctables.h @@ -21,17 +21,19 @@ */ -#ifndef CTABLES_H -#define CTABLES_H +#pragma once -/** Used to indicate command success codes - */ +/** Used to indicate the result of trying to execute a command. */ enum CmdResult { - CMD_FAILURE = 0, /* Command exists, but failed */ - CMD_SUCCESS = 1, /* Command exists, and succeeded */ - CMD_INVALID = 2, /* Command doesnt exist at all! */ - CMD_EPERM = 3 /* Command failed because of a permission check */ + /** The command exists but its execution failed. */ + CMD_FAILURE = 0, + + /** The command exists and its execution succeeded. */ + CMD_SUCCESS = 1, + + /* The command does not exist. */ + CMD_INVALID = 2 }; /** Flag for commands that are only allowed from servers */ @@ -44,7 +46,6 @@ const char FLAG_SERVERONLY = 7; // technically anything nonzero below 'A' works */ enum TranslateType { - TR_END, /* End of known parameters, everything after this is TR_TEXT */ TR_TEXT, /* Raw text, leave as-is */ TR_NICK, /* Nickname, translate to UUID for server->server */ TR_CUSTOM /* Custom translation handled by EncodeParameter/DecodeParameter */ @@ -77,10 +78,17 @@ struct RouteDescriptor */ std::string serverdest; + /** For unicast, the destination Server + */ + Server* server; + /** Create a RouteDescriptor */ RouteDescriptor(RouteType t, const std::string &d) - : type(t), serverdest(d) { } + : type(t), serverdest(d), server(NULL) { } + + RouteDescriptor(RouteType t, Server* srv) + : type(t), server(srv) { } }; /** Do not route this command */ @@ -99,12 +107,47 @@ struct RouteDescriptor /** A structure that defines a command. Every command available * in InspIRCd must be defined as derived from Command. */ -class CoreExport Command : public ServiceProvider +class CoreExport CommandBase : public ServiceProvider { public: + /** Encapsulates parameters to a command. */ + class Params : public std::vector<std::string> + { + private: + /* IRCv3 message tags. */ + ClientProtocol::TagMap tags; + + public: + /** Initializes a new instance from parameter and tag references. + * @param paramsref Message parameters. + * @param tagsref IRCv3 message tags. + */ + Params(const std::vector<std::string>& paramsref, const ClientProtocol::TagMap& tagsref) + : std::vector<std::string>(paramsref) + , tags(tagsref) + { + } + + /** Initializes a new instance from parameter iterators. + * @param first The first element in the parameter array. + * @param last The last element in the parameter array. + */ + template<typename Iterator> + Params(Iterator first, Iterator last) + : std::vector<std::string>(first, last) + { + } + + /** Initializes a new empty instance. */ + Params() { } + + /** Retrieves the IRCv3 message tags. */ + const ClientProtocol::TagMap& GetTags() const { return tags; } + }; + /** User flags needed to execute the command or 0 */ - char flags_needed; + unsigned char flags_needed; /** Minimum number of parameters command takes */ @@ -120,10 +163,6 @@ class CoreExport Command : public ServiceProvider */ unsigned long use_count; - /** used by /stats m - */ - unsigned long total_bytes; - /** True if the command is disabled to non-opers */ bool disabled; @@ -153,7 +192,7 @@ class CoreExport Command : public ServiceProvider /** How many seconds worth of penalty does this command have? */ - int Penalty; + unsigned int Penalty; /** Create a new command. * @param me The module which created this command. @@ -162,43 +201,16 @@ class CoreExport Command : public ServiceProvider * @param maxpara Maximum number of parameters this command may have - extra parameters * will be tossed into one last space-seperated param. */ - Command(Module* me, const std::string &cmd, int minpara = 0, int maxpara = 0) : - ServiceProvider(me, cmd, SERVICE_COMMAND), flags_needed(0), min_params(minpara), max_params(maxpara), - use_count(0), total_bytes(0), disabled(false), works_before_reg(false), allow_empty_last_param(true), - Penalty(1) - { - } + CommandBase(Module* me, const std::string& cmd, unsigned int minpara = 0, unsigned int maxpara = 0); - /** Handle the command from a user. - * @param parameters The parameters for the command. - * @param user The user who issued the command. - * @return Return CMD_SUCCESS on success, or CMD_FAILURE on failure. - */ - virtual CmdResult Handle(const std::vector<std::string>& parameters, User* user) = 0; - - virtual RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters) - { - return ROUTE_LOCALONLY; - } + virtual RouteDescriptor GetRouting(User* user, const CommandBase::Params& parameters); /** Encode a parameter for server->server transmission. * Used for parameters for which the translation type is TR_CUSTOM. * @param parameter The parameter to encode. Can be modified in place. * @param index The parameter index (0 == first parameter). */ - virtual void EncodeParameter(std::string& parameter, int index) - { - } - - /** Decode a parameter from server->server transmission. - * Not currently used in this version of InspIRCd. - * Used for parameters for which the translation type is TR_CUSTOM. - * @param parameter The parameter to decode. Can be modified in place. - * @param index The parameter index (0 == first parameter). - */ - virtual void DecodeParameter(std::string& parameter, int index) - { - } + virtual void EncodeParameter(std::string& parameter, unsigned int index); /** Disable or enable this command. * @param setting True to disable the command. @@ -224,18 +236,45 @@ class CoreExport Command : public ServiceProvider return works_before_reg; } - virtual ~Command(); + virtual ~CommandBase(); +}; + +class CoreExport Command : public CommandBase +{ + public: + /** If true, the command will not be forwarded by the linking module even if it comes via ENCAP. + * Can be used to forward commands before their effects. + */ + bool force_manual_route; + + Command(Module* me, const std::string& cmd, unsigned int minpara = 0, unsigned int maxpara = 0); + + /** Handle the command from a user. + * @param parameters The parameters for the command. + * @param user The user who issued the command. + * @return Return CMD_SUCCESS on success, or CMD_FAILURE on failure. + */ + virtual CmdResult Handle(User* user, const Params& parameters) = 0; + + /** Register this object in the CommandParser + */ + void RegisterService() CXX11_OVERRIDE; + + /** Destructor + * Removes this command from the command parser + */ + ~Command(); }; class CoreExport SplitCommand : public Command { public: - SplitCommand(Module* me, const std::string &cmd, int minpara = 0, int maxpara = 0) + SplitCommand(Module* me, const std::string &cmd, unsigned int minpara = 0, unsigned int maxpara = 0) : Command(me, cmd, minpara, maxpara) {} - virtual CmdResult Handle(const std::vector<std::string>& parameters, User* user); - virtual CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser* user); - virtual CmdResult HandleRemote(const std::vector<std::string>& parameters, RemoteUser* user); - virtual CmdResult HandleServer(const std::vector<std::string>& parameters, FakeUser* user); + CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE; + virtual CmdResult HandleLocal(LocalUser* user, const Params& parameters); + virtual CmdResult HandleRemote(RemoteUser* user, const Params& parameters); + virtual CmdResult HandleServer(FakeUser* user, const Params& parameters); }; /** Shortcut macros for defining translation lists @@ -252,5 +291,3 @@ class CoreExport SplitCommand : public Command translation.push_back(x5);translation.push_back(x6);translation.push_back(x7); #define TRANSLATE8(x1,x2,x3,x4,x5,x6,x7,x8) translation.push_back(x1);translation.push_back(x2);translation.push_back(x3);translation.push_back(x4);\ translation.push_back(x5);translation.push_back(x6);translation.push_back(x7);translation.push_back(x8); - -#endif diff --git a/include/cull_list.h b/include/cull_list.h index 75b08b7a3..f5087f512 100644 --- a/include/cull_list.h +++ b/include/cull_list.h @@ -20,8 +20,7 @@ */ -#ifndef CULL_LIST_H -#define CULL_LIST_H +#pragma once /** * The CullList class is used to delete objects at the end of the main loop to @@ -44,20 +43,25 @@ class CoreExport CullList void Apply(); }; +/** Represents an action which is executable by an action list */ +class CoreExport ActionBase : public classbase +{ + public: + /** Executes this action. */ + virtual void Call() = 0; +}; + class CoreExport ActionList { - std::vector<HandlerBase0<void>*> list; + std::vector<ActionBase*> list; public: /** Adds an item to the list */ - void AddAction(HandlerBase0<void>* item) { list.push_back(item); } + void AddAction(ActionBase* item) { list.push_back(item); } /** Runs the items */ void Run(); }; - -#endif - diff --git a/include/dns.h b/include/dns.h deleted file mode 100644 index 05df6f69c..000000000 --- a/include/dns.h +++ /dev/null @@ -1,455 +0,0 @@ -/* - * InspIRCd -- Internet Relay Chat Daemon - * - * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org> - * Copyright (C) 2005-2008 Craig Edwards <craigedwards@brainbox.cc> - * Copyright (C) 2007 Dennis Friis <peavey@inspircd.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/>. - */ - - -/* -dns.h - dns library very very loosely based on -firedns, Copyright (C) 2002 Ian Gulliver - -This program is free software; you can redistribute it and/or modify -it under the terms of version 2 of the GNU General Public License as -published by the Free Software Foundation. - -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, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#ifndef DNS_H -#define DNS_H - -#include "socket.h" -#include "hashcomp.h" - -/** - * Query and resource record types - */ -enum QueryType -{ - /** Uninitialized Query */ - DNS_QUERY_NONE = 0, - /** 'A' record: an ipv4 address */ - DNS_QUERY_A = 1, - /** 'CNAME' record: An alias */ - DNS_QUERY_CNAME = 5, - /** 'PTR' record: a hostname */ - DNS_QUERY_PTR = 12, - /** 'AAAA' record: an ipv6 address */ - DNS_QUERY_AAAA = 28, - - /** Force 'PTR' to use IPV4 scemantics */ - DNS_QUERY_PTR4 = 0xFFFD, - /** Force 'PTR' to use IPV6 scemantics */ - DNS_QUERY_PTR6 = 0xFFFE -}; - -/** - * Result status, used internally - */ -class CoreExport DNSResult -{ - public: - /** Result ID - */ - int id; - /** Result body, a hostname or IP address - */ - std::string result; - /** Time-to-live value of the result - */ - unsigned long ttl; - /** The original request, a hostname or IP address - */ - std::string original; - /** The type of the request - */ - QueryType type; - - /** Build a DNS result. - * @param i The request ID - * @param res The request result, a hostname or IP - * @param timetolive The request time-to-live - * @param orig The original request, a hostname or IP - * @param qt The type of DNS query this result represents. - */ - DNSResult(int i, const std::string &res, unsigned long timetolive, const std::string &orig, QueryType qt = DNS_QUERY_NONE) : id(i), result(res), ttl(timetolive), original(orig), type(qt) { } -}; - -/** - * Information on a completed lookup, used internally - */ -typedef std::pair<unsigned char*, std::string> DNSInfo; - -/** Cached item stored in the query cache. - */ -class CoreExport CachedQuery -{ - public: - /** The cached result data, an IP or hostname - */ - std::string data; - /** The type of result this is - */ - QueryType type; - /** The time when the item is due to expire - */ - time_t expires; - - /** Build a cached query - * @param res The result data, an IP or hostname - * @param qt The type of DNS query this instance represents. - * @param ttl The time-to-live value of the query result - */ - CachedQuery(const std::string &res, QueryType qt, unsigned int ttl); - - /** Returns the number of seconds remaining before this - * cache item has expired and should be removed. - */ - int CalcTTLRemaining(); -}; - -/** DNS cache information. Holds IPs mapped to hostnames, and hostnames mapped to IPs. - */ -typedef nspace::hash_map<irc::string, CachedQuery, irc::hash> dnscache; - -/** - * Error types that class Resolver can emit to its error method. - */ -enum ResolverError -{ - RESOLVER_NOERROR = 0, - RESOLVER_NSDOWN = 1, - RESOLVER_NXDOMAIN = 2, - RESOLVER_BADIP = 3, - RESOLVER_TIMEOUT = 4, - RESOLVER_FORCEUNLOAD = 5 -}; - -/** - * Used internally to force PTR lookups to use a certain protocol scemantics, - * e.g. x.x.x.x.in-addr.arpa for v4, and *.ip6.arpa for v6. - */ -enum ForceProtocol -{ - /** Forced to use ipv4 */ - PROTOCOL_IPV4 = 0, - /** Forced to use ipv6 */ - PROTOCOL_IPV6 = 1 -}; - -/** - * The Resolver class is a high-level abstraction for resolving DNS entries. - * It can do forward and reverse IPv4 lookups, and where IPv6 is supported, will - * also be able to do those, transparent of protocols. Module developers must - * extend this class via inheritence, and then insert a pointer to their derived - * class into the core using Server::AddResolver(). Once you have done this, - * the class will be able to receive callbacks. There are two callbacks which - * can occur by calling virtual methods, one is a success situation, and the other - * an error situation. - */ -class CoreExport Resolver -{ - protected: - /** - * Pointer to creator module (if any, or NULL) - */ - ModuleRef Creator; - /** - * The input data, either a host or an IP address - */ - std::string input; - /** - * True if a forward lookup is being performed, false if otherwise - */ - QueryType querytype; - /** - * The DNS erver being used for lookups. If this is an empty string, - * the value of ServerConfig::DNSServer is used instead. - */ - std::string server; - /** - * The ID allocated to your lookup. This is a pseudo-random number - * between 0 and 65535, a value of -1 indicating a failure. - * The core uses this to route results to the correct objects. - */ - int myid; - - /** - * Cached result, if there is one - */ - CachedQuery *CQ; - - /** - * Time left before cache expiry - */ - int time_left; - - public: - /** - * Initiate DNS lookup. Your class should not attempt to delete or free these - * objects, as the core will do this for you. They must always be created upon - * the heap using new, as you cannot be sure at what time they will be deleted. - * Allocating them on the stack or attempting to delete them yourself could cause - * the object to go 'out of scope' and cause a segfault in the core if the result - * arrives at a later time. - * @param source The IP or hostname to resolve - * @param qt The query type to perform. Resolution of 'A', 'AAAA', 'PTR' and 'CNAME' records - * is supported. Use one of the QueryType enum values to initiate this type of - * lookup. Resolution of 'AAAA' ipv6 records is always supported, regardless of - * wether InspIRCd is built with ipv6 support. - * To look up reverse records, specify one of DNS_QUERY_PTR4 or DNS_QUERY_PTR6 depending - * on the type of address you are looking up. - * @param cached The constructor will set this boolean to true or false depending - * on whether the DNS lookup you are attempting is cached (and not expired) or not. - * If the value is cached, upon return this will be set to true, otherwise it will - * be set to false. You should pass this value to InspIRCd::AddResolver(), which - * will then influence the behaviour of the method and determine whether a cached - * or non-cached result is obtained. The value in this variable is always correct - * for the given request when the constructor exits. - * @param creator See the note below. - * @throw ModuleException This class may throw an instance of ModuleException, in the - * event a lookup could not be allocated, or a similar hard error occurs such as - * the network being down. This will also be thrown if an invalid IP address is - * passed when resolving a 'PTR' record. - * - * NOTE: If you are instantiating your DNS lookup from a module, you should set the - * value of creator to point at your Module class. This way if your module is unloaded - * whilst lookups are in progress, they can be safely removed and your module will not - * crash the server. - */ - Resolver(const std::string &source, QueryType qt, bool &cached, Module* creator); - - /** - * The default destructor does nothing. - */ - virtual ~Resolver(); - - /** - * When your lookup completes, this method will be called. - * @param result The resulting DNS lookup, either an IP address or a hostname. - * @param ttl The time-to-live value of the result, in the instance of a cached - * result, this is the number of seconds remaining before refresh/expiry. - * @param cached True if the result is a cached result, false if it was requested - * from the DNS server. - */ - virtual void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached) = 0; - - /** - * If an error occurs (such as NXDOMAIN, no domain name found) then this method - * will be called. - * @param e A ResolverError enum containing the error type which has occured. - * @param errormessage The error text of the error that occured. - */ - virtual void OnError(ResolverError e, const std::string &errormessage); - - /** - * Returns the id value of this class. This is primarily used by the core - * to determine where in various tables to place a pointer to your class, but it - * is safe to call and use this method. - * As specified in RFC1035, each dns request has a 16 bit ID value, ranging - * from 0 to 65535. If there is an issue and the core cannot send your request, - * this method will return -1. - */ - int GetId(); - - /** - * Returns the creator module, or NULL - */ - Module* GetCreator(); - - /** - * If the result is a cached result, this triggers the objects - * OnLookupComplete. This is done because it is not safe to call - * the abstract virtual method from the constructor. - */ - void TriggerCachedResult(); -}; - -/** DNS is a singleton class used by the core to dispatch dns - * requests to the dns server, and route incoming dns replies - * back to Resolver objects, based upon the request ID. You - * should never use this class yourself. - */ -class CoreExport DNS : public EventHandler -{ - private: - - /** - * The maximum value of a dns request id, - * 16 bits wide, 0xFFFF. - */ - static const int MAX_REQUEST_ID = 0xFFFF; - - /** Maximum number of entries in cache - */ - static const unsigned int MAX_CACHE_SIZE = 1000; - - /** - * Currently cached items - */ - dnscache* cache; - - /** A timer which ticks every hour to remove expired - * items from the DNS cache. - */ - class CacheTimer* PruneTimer; - - /** - * Build a dns packet payload - */ - int MakePayload(const char* name, const QueryType rr, const unsigned short rr_class, unsigned char* payload); - - public: - - irc::sockets::sockaddrs myserver; - - /** - * Currently active Resolver classes - */ - Resolver* Classes[MAX_REQUEST_ID]; - - /** - * Requests that are currently 'in flight' - */ - DNSRequest* requests[MAX_REQUEST_ID]; - - /** - * The port number DNS requests are made on, - * and replies have as a source-port number. - */ - static const int QUERY_PORT = 53; - - /** - * Fill an rr (resource record) with data from input - */ - static void FillResourceRecord(ResourceRecord* rr, const unsigned char* input); - - /** - * Fill a header with data from input limited by a length - */ - static void FillHeader(DNSHeader *header, const unsigned char *input, const int length); - - /** - * Empty out a header into a data stream ready for transmission "on the wire" - */ - static void EmptyHeader(unsigned char *output, const DNSHeader *header, const int length); - - /** - * Start the lookup of an ipv4 from a hostname - */ - int GetIP(const char* name); - - /** - * Start lookup of a hostname from an ip, but - * force a specific protocol to be used for the lookup - * for example to perform an ipv6 reverse lookup. - */ - int GetNameForce(const char *ip, ForceProtocol fp); - - /** - * Start lookup of an ipv6 from a hostname - */ - int GetIP6(const char *name); - - /** - * Start lookup of a CNAME from another hostname - */ - int GetCName(const char* alias); - - /** - * Fetch the result string (an ip or host) - * and/or an error message to go with it. - */ - DNSResult GetResult(); - - /** - * Handle a SocketEngine read event - * Inherited from EventHandler - */ - void HandleEvent(EventType et, int errornum = 0); - - /** - * Add a Resolver* to the list of active classes - */ - bool AddResolverClass(Resolver* r); - - /** - * Add a query to the list to be sent - */ - DNSRequest* AddQuery(DNSHeader *header, int &id, const char* original); - - /** - * The constructor initialises the dns socket, - * and clears the request lists. - */ - DNS(); - - /** - * Re-initialize the DNS subsystem. - */ - void Rehash(); - - /** - * Destructor - */ - ~DNS(); - - /** - * Turn an in6_addr into a .ip6.arpa domain - */ - static void MakeIP6Int(char* query, const in6_addr *ip); - - /** - * Clean out all dns resolvers owned by a particular - * module, to make unloading a module safe if there - * are dns requests currently in progress. - */ - void CleanResolvers(Module* module); - - /** Return the cached value of an IP or hostname - * @param source An IP or hostname to find in the cache. - * @return A pointer to a CachedQuery if the item exists, - * otherwise NULL. - */ - CachedQuery* GetCache(const std::string &source); - - /** Delete a cached item from the DNS cache. - * @param source An IP or hostname to remove - */ - void DelCache(const std::string &source); - - /** Clear all items from the DNS cache immediately. - */ - int ClearCache(); - - /** Prune the DNS cache, e.g. remove all expired - * items and rehash the cache buckets, but leave - * items in the hash which are still valid. - */ - int PruneCache(); -}; - -#endif - diff --git a/include/dynamic.h b/include/dynamic.h index bbe89dc7e..905eb479e 100644 --- a/include/dynamic.h +++ b/include/dynamic.h @@ -20,8 +20,7 @@ */ -#ifndef DLL_H -#define DLL_H +#pragma once /** The DLLManager class is able to load a module file by filename, * and locate its init_module symbol. @@ -63,6 +62,3 @@ class CoreExport DLLManager : public classbase /** Get detailed version information from the module file */ std::string GetVersion(); }; - -#endif - diff --git a/include/dynref.h b/include/dynref.h new file mode 100644 index 000000000..6e2e17423 --- /dev/null +++ b/include/dynref.h @@ -0,0 +1,135 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2013 Attila Molnar <attilamolnar@hush.com> + * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.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/>. + */ + + +#pragma once + +#include "base.h" + +class CoreExport dynamic_reference_base : public interfacebase, public insp::intrusive_list_node<dynamic_reference_base> +{ + public: + class CaptureHook + { + public: + /** Called when the target of the dynamic_reference has been acquired + */ + virtual void OnCapture() = 0; + }; + + private: + std::string name; + CaptureHook* hook; + void resolve(); + protected: + ServiceProvider* value; + public: + ModuleRef creator; + dynamic_reference_base(Module* Creator, const std::string& Name); + ~dynamic_reference_base(); + inline const std::string& GetProvider() { return name; } + void SetProvider(const std::string& newname); + + /** Set handler to call when the target object becomes available + * @param h Handler to call + */ + void SetCaptureHook(CaptureHook* h) { hook = h; } + + void check(); + operator bool() { return (value != NULL); } + static void reset_all(); +}; + +inline void dynamic_reference_base::check() +{ + if (!value) + throw ModuleException("Dynamic reference to '" + name + "' failed to resolve"); +} + +template<typename T> +class dynamic_reference : public dynamic_reference_base +{ + public: + dynamic_reference(Module* Creator, const std::string& Name) + : dynamic_reference_base(Creator, Name) {} + + inline T* operator->() + { + check(); + return static_cast<T*>(value); + } + + T* operator*() + { + return operator->(); + } + + const T* operator->() const + { + return static_cast<T*>(value); + } + + const T* operator*() const + { + return operator->(); + } +}; + +template<typename T> +class dynamic_reference_nocheck : public dynamic_reference_base +{ + public: + dynamic_reference_nocheck(Module* Creator, const std::string& Name) + : dynamic_reference_base(Creator, Name) {} + + T* operator->() + { + return static_cast<T*>(value); + } + + T* operator*() + { + return operator->(); + } + + const T* operator->() const + { + return static_cast<T*>(value); + } + + const T* operator*() const + { + return operator->(); + } +}; + +class ModeHandler; +class ChanModeReference : public dynamic_reference_nocheck<ModeHandler> +{ + public: + ChanModeReference(Module* mod, const std::string& modename) + : dynamic_reference_nocheck<ModeHandler>(mod, "mode/" + modename) {} +}; + +class UserModeReference : public dynamic_reference_nocheck<ModeHandler> +{ + public: + UserModeReference(Module* mod, const std::string& modename) + : dynamic_reference_nocheck<ModeHandler>(mod, "umode/" + modename) {} +}; diff --git a/include/event.h b/include/event.h new file mode 100644 index 000000000..73a45a541 --- /dev/null +++ b/include/event.h @@ -0,0 +1,163 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com> + * + * 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/>. + */ + + +#pragma once + +namespace Events +{ + class ModuleEventListener; + class ModuleEventProvider; +} + +/** Provider of one or more cross-module events. + * Modules who wish to provide events for other modules create instances of this class and use + * one of the macros below to fire the event, passing the instance of the event provider class + * to the macro. + * Event providers are identified using a unique identifier string. + */ +class Events::ModuleEventProvider : public ServiceProvider, private dynamic_reference_base::CaptureHook +{ + public: + struct Comp + { + bool operator()(ModuleEventListener* one, ModuleEventListener* two) const; + }; + + typedef insp::flat_multiset<ModuleEventListener*, Comp> SubscriberList; + + /** Constructor + * @param mod Module providing the event(s) + * @param eventid Identifier of the event or event group provided, must be unique + */ + ModuleEventProvider(Module* mod, const std::string& eventid) + : ServiceProvider(mod, eventid, SERVICE_DATA) + , prov(mod, eventid) + { + prov.SetCaptureHook(this); + } + + /** Get list of objects subscribed to this event + * @return List of subscribed objects + */ + const SubscriberList& GetSubscribers() const { return prov->subscribers; } + + friend class ModuleEventListener; + + private: + void OnCapture() CXX11_OVERRIDE + { + // If someone else holds the list from now on, clear mine. See below for more info. + if (*prov != this) + subscribers.clear(); + } + + /** Reference to the active provider for this event. In case multiple event providers + * exist for the same event, only one of them contains the list of subscribers. + * To handle the case when we are not the ones with the list, we get it from the provider + * where the dynref points to. + */ + dynamic_reference_nocheck<ModuleEventProvider> prov; + + /** List of objects subscribed to the event(s) provided by us, or empty if multiple providers + * exist with the same name and we are not the ones holding the list. + */ + SubscriberList subscribers; +}; + +/** Base class for abstract classes describing cross-module events. + * Subscribers should NOT inherit directly from this class. + */ +class Events::ModuleEventListener : private dynamic_reference_base::CaptureHook +{ + /** Reference to the provider, can be NULL if none of the provider modules are loaded + */ + dynamic_reference_nocheck<ModuleEventProvider> prov; + + const unsigned int eventpriority; + + /** Called by the dynref when the event provider becomes available + */ + void OnCapture() CXX11_OVERRIDE + { + prov->subscribers.insert(this); + } + + public: + static const unsigned int DefaultPriority = 100; + + /** Constructor + * @param mod Module subscribing + * @param eventid Identifier of the event to subscribe to + */ + ModuleEventListener(Module* mod, const std::string& eventid, unsigned int eventprio = DefaultPriority) + : prov(mod, eventid) + , eventpriority(eventprio) + { + prov.SetCaptureHook(this); + // If the dynamic_reference resolved at construction our capture handler wasn't called + if (prov) + ModuleEventListener::OnCapture(); + } + + ~ModuleEventListener() + { + if (prov) + prov->subscribers.erase(this); + } + + friend struct ModuleEventProvider::Comp; +}; + +inline bool Events::ModuleEventProvider::Comp::operator()(Events::ModuleEventListener* one, Events::ModuleEventListener* two) const +{ + return (one->eventpriority < two->eventpriority); +} + +/** + * Run the given hook provided by a module + * + * FOREACH_MOD_CUSTOM(accountevprov, AccountEventListener, OnAccountChange, MOD_RESULT, (user, newaccount)) + */ +#define FOREACH_MOD_CUSTOM(prov, listenerclass, func, params) do { \ + const ::Events::ModuleEventProvider::SubscriberList& _handlers = (prov).GetSubscribers(); \ + for (::Events::ModuleEventProvider::SubscriberList::const_iterator _i = _handlers.begin(); _i != _handlers.end(); ++_i) \ + { \ + listenerclass* _t = static_cast<listenerclass*>(*_i); \ + _t->func params ; \ + } \ +} while (0); + +/** + * Run the given hook provided by a module until some module returns MOD_RES_ALLOW or MOD_RES_DENY. + * If no module does that, result is set to MOD_RES_PASSTHRU. + * + * Example: ModResult MOD_RESULT; + * FIRST_MOD_RESULT_CUSTOM(httpevprov, HTTPRequestEventListener, OnHTTPRequest, MOD_RESULT, (request)); + */ +#define FIRST_MOD_RESULT_CUSTOM(prov, listenerclass, func, result, params) do { \ + result = MOD_RES_PASSTHRU; \ + const ::Events::ModuleEventProvider::SubscriberList& _handlers = (prov).GetSubscribers(); \ + for (::Events::ModuleEventProvider::SubscriberList::const_iterator _i = _handlers.begin(); _i != _handlers.end(); ++_i) \ + { \ + listenerclass* _t = static_cast<listenerclass*>(*_i); \ + result = _t->func params ; \ + if (result != MOD_RES_PASSTHRU) \ + break; \ + } \ +} while (0); diff --git a/include/exitcodes.h b/include/exitcodes.h index d4890c94d..b1090d141 100644 --- a/include/exitcodes.h +++ b/include/exitcodes.h @@ -19,8 +19,7 @@ */ -#ifndef EXITCODE_H -#define EXITCODE_H +#pragma once /** Valid exit codes to be used with InspIRCd::Exit() */ @@ -28,30 +27,18 @@ enum ExitStatus { EXIT_STATUS_NOERROR = 0, /* No error */ EXIT_STATUS_DIE = 1, /* Operator issued DIE */ - EXIT_STATUS_FAILED_EXEC = 2, /* execv() failed */ - EXIT_STATUS_INTERNAL = 3, /* Internal error */ - EXIT_STATUS_CONFIG = 4, /* Config error */ - EXIT_STATUS_LOG = 5, /* Log file error */ - EXIT_STATUS_FORK = 6, /* fork() failed */ - EXIT_STATUS_ARGV = 7, /* Invalid program arguments */ - EXIT_STATUS_BIND = 8, /* Port binding failed on all ports */ - EXIT_STATUS_PID = 9, /* Couldn't write PID file */ - EXIT_STATUS_SOCKETENGINE = 10, /* Couldn't start socket engine */ - EXIT_STATUS_ROOT = 11, /* Refusing to start as root */ - EXIT_STATUS_DIETAG = 12, /* Found a die tag in the config file */ - EXIT_STATUS_MODULE = 13, /* Couldn't load a required module */ - EXIT_STATUS_CREATEPROCESS = 14, /* CreateProcess failed (windows) */ - EXIT_STATUS_SIGTERM = 15, /* Note: dont move this value. It corresponds with the value of #define SIGTERM. */ - EXIT_STATUS_BADHANDLER = 16, /* Bad command handler loaded */ - EXIT_STATUS_RSCH_FAILED = 17, /* Windows service specific failure, will name these later */ - EXIT_STATUS_UPDATESCM_FAILED = 18, /* Windows service specific failure, will name these later */ - EXIT_STATUS_CREATE_EVENT_FAILED = 19 /* Windows service specific failure, will name these later */ + EXIT_STATUS_CONFIG = 2, /* Config error */ + EXIT_STATUS_LOG = 3, /* Log file error */ + EXIT_STATUS_FORK = 4, /* fork() failed */ + EXIT_STATUS_ARGV = 5, /* Invalid program arguments */ + EXIT_STATUS_PID = 6, /* Couldn't write PID file */ + EXIT_STATUS_SOCKETENGINE = 7, /* Couldn't start socket engine */ + EXIT_STATUS_ROOT = 8, /* Refusing to start as root */ + EXIT_STATUS_MODULE = 9, /* Couldn't load a required module */ + EXIT_STATUS_SIGTERM = 10 /* Received SIGTERM */ }; /** Array that maps exit codes (ExitStatus types) to * human-readable strings to be shown on shutdown. */ extern const char * ExitCodes[]; - -#endif - diff --git a/include/extensible.h b/include/extensible.h index bcc4992bb..f88ede461 100644 --- a/include/extensible.h +++ b/include/extensible.h @@ -17,10 +17,7 @@ */ -#ifndef EXTENSIBLE_H -#define EXTENSIBLE_H - -#include <stdint.h> +#pragma once enum SerializeFormat { @@ -39,7 +36,20 @@ enum SerializeFormat class CoreExport ExtensionItem : public ServiceProvider, public usecountbase { public: - ExtensionItem(const std::string& key, Module* owner); + /** Extensible subclasses + */ + enum ExtensibleType + { + EXT_USER, + EXT_CHANNEL, + EXT_MEMBERSHIP + }; + + /** Type (subclass) of Extensible that this ExtensionItem is valid for + */ + const ExtensibleType type; + + ExtensionItem(const std::string& key, ExtensibleType exttype, Module* owner); virtual ~ExtensionItem(); /** Serialize this item into a string * @@ -55,7 +65,11 @@ class CoreExport ExtensionItem : public ServiceProvider, public usecountbase */ virtual void unserialize(SerializeFormat format, Extensible* container, const std::string& value) = 0; /** Free the item */ - virtual void free(void* item) = 0; + virtual void free(Extensible* container, void* item) = 0; + + /** Register this object in the ExtensionManager + */ + void RegisterService() CXX11_OVERRIDE; protected: /** Get the item from the internal map */ @@ -76,7 +90,7 @@ class CoreExport ExtensionItem : public ServiceProvider, public usecountbase class CoreExport Extensible : public classbase { public: - typedef std::map<reference<ExtensionItem>,void*> ExtensibleStore; + typedef insp::flat_map<reference<ExtensionItem>, void*> ExtensibleStore; // Friend access for the protected getter/setter friend class ExtensionItem; @@ -85,6 +99,11 @@ class CoreExport Extensible : public classbase * Holds all extensible metadata for the class. */ ExtensibleStore extensions; + + /** True if this Extensible has been culled. + * A warning is generated if false on destruction. + */ + unsigned int culled:1; public: /** * Get the extension items for iteraton (i.e. for metadata sync during netburst) @@ -92,36 +111,51 @@ class CoreExport Extensible : public classbase inline const ExtensibleStore& GetExtList() const { return extensions; } Extensible(); - virtual CullResult cull(); + CullResult cull() CXX11_OVERRIDE; virtual ~Extensible(); void doUnhookExtensions(const std::vector<reference<ExtensionItem> >& toRemove); + + /** + * Free all extension items attached to this Extensible + */ + void FreeAllExtItems(); }; class CoreExport ExtensionManager { - std::map<std::string, reference<ExtensionItem> > types; public: + typedef std::map<std::string, reference<ExtensionItem> > ExtMap; + bool Register(ExtensionItem* item); void BeginUnregister(Module* module, std::vector<reference<ExtensionItem> >& list); ExtensionItem* GetItem(const std::string& name); + + /** Get all registered extensions keyed by their names + * @return Const map of ExtensionItem pointers keyed by their names + */ + const ExtMap& GetExts() const { return types; } + + private: + ExtMap types; }; /** Base class for items that are NOT synchronized between servers */ class CoreExport LocalExtItem : public ExtensionItem { public: - LocalExtItem(const std::string& key, Module* owner); + LocalExtItem(const std::string& key, ExtensibleType exttype, Module* owner); virtual ~LocalExtItem(); - virtual std::string serialize(SerializeFormat format, const Extensible* container, void* item) const; - virtual void unserialize(SerializeFormat format, Extensible* container, const std::string& value); - virtual void free(void* item) = 0; + std::string serialize(SerializeFormat format, const Extensible* container, void* item) const CXX11_OVERRIDE; + void unserialize(SerializeFormat format, Extensible* container, const std::string& value) CXX11_OVERRIDE; + void free(Extensible* container, void* item) CXX11_OVERRIDE = 0; }; -template<typename T> +template <typename T, typename Del = stdalgo::defaultdeleter<T> > class SimpleExtItem : public LocalExtItem { public: - SimpleExtItem(const std::string& Key, Module* parent) : LocalExtItem(Key, parent) + SimpleExtItem(const std::string& Key, ExtensibleType exttype, Module* parent) + : LocalExtItem(Key, exttype, parent) { } @@ -138,57 +172,62 @@ class SimpleExtItem : public LocalExtItem { T* ptr = new T(value); T* old = static_cast<T*>(set_raw(container, ptr)); - delete old; + Del del; + del(old); } inline void set(Extensible* container, T* value) { T* old = static_cast<T*>(set_raw(container, value)); - delete old; + Del del; + del(old); } inline void unset(Extensible* container) { T* old = static_cast<T*>(unset_raw(container)); - delete old; + Del del; + del(old); } - virtual void free(void* item) + void free(Extensible* container, void* item) CXX11_OVERRIDE { - delete static_cast<T*>(item); + Del del; + del(static_cast<T*>(item)); } }; class CoreExport LocalStringExt : public SimpleExtItem<std::string> { public: - LocalStringExt(const std::string& key, Module* owner); + LocalStringExt(const std::string& key, ExtensibleType exttype, Module* owner); virtual ~LocalStringExt(); - std::string serialize(SerializeFormat format, const Extensible* container, void* item) const; + std::string serialize(SerializeFormat format, const Extensible* container, void* item) const CXX11_OVERRIDE; + void unserialize(SerializeFormat format, Extensible* container, const std::string& value) CXX11_OVERRIDE; }; class CoreExport LocalIntExt : public LocalExtItem { public: - LocalIntExt(const std::string& key, Module* owner); + LocalIntExt(const std::string& key, ExtensibleType exttype, Module* owner); virtual ~LocalIntExt(); - std::string serialize(SerializeFormat format, const Extensible* container, void* item) const; + std::string serialize(SerializeFormat format, const Extensible* container, void* item) const CXX11_OVERRIDE; + void unserialize(SerializeFormat format, Extensible* container, const std::string& value) CXX11_OVERRIDE; intptr_t get(const Extensible* container) const; intptr_t set(Extensible* container, intptr_t value); - void free(void* item); + void unset(Extensible* container) { set(container, 0); } + void free(Extensible* container, void* item) CXX11_OVERRIDE; }; class CoreExport StringExtItem : public ExtensionItem { public: - StringExtItem(const std::string& key, Module* owner); + StringExtItem(const std::string& key, ExtensibleType exttype, Module* owner); virtual ~StringExtItem(); std::string* get(const Extensible* container) const; - std::string serialize(SerializeFormat format, const Extensible* container, void* item) const; - void unserialize(SerializeFormat format, Extensible* container, const std::string& value); + std::string serialize(SerializeFormat format, const Extensible* container, void* item) const CXX11_OVERRIDE; + void unserialize(SerializeFormat format, Extensible* container, const std::string& value) CXX11_OVERRIDE; void set(Extensible* container, const std::string& value); void unset(Extensible* container); - void free(void* item); + void free(Extensible* container, void* item) CXX11_OVERRIDE; }; - -#endif diff --git a/include/filelogger.h b/include/filelogger.h index 22a94c934..af43a6d90 100644 --- a/include/filelogger.h +++ b/include/filelogger.h @@ -18,27 +18,10 @@ */ -#ifndef FILELOGGER_H -#define FILELOGGER_H +#pragma once #include "logger.h" -/** Debug levels for use with InspIRCd::Log() - * */ -enum DebugLevel -{ - RAWIO = 5, - DEBUG = 10, - VERBOSE = 20, - DEFAULT = 30, - SPARSE = 40, - NONE = 50 -}; - - -/* Forward declaration -- required */ -class InspIRCd; - /** A logging class which logs to a streamed file. */ class CoreExport FileLogStream : public LogStream @@ -46,12 +29,9 @@ class CoreExport FileLogStream : public LogStream private: FileWriter *f; public: - FileLogStream(int loglevel, FileWriter *fw); + FileLogStream(LogLevel loglevel, FileWriter *fw); virtual ~FileLogStream(); - virtual void OnLog(int loglevel, const std::string &type, const std::string &msg); + void OnLog(LogLevel loglevel, const std::string& type, const std::string& msg) CXX11_OVERRIDE; }; - -#endif - diff --git a/include/fileutils.h b/include/fileutils.h new file mode 100644 index 000000000..89f92f97f --- /dev/null +++ b/include/fileutils.h @@ -0,0 +1,87 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2013 Peter Powell <petpow@saberuk.com> + * + * 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/>. + */ + + +#pragma once + +/** Provides an easy method of reading a text file into memory. */ +class CoreExport FileReader +{ + /** The lines of text in the file. */ + std::vector<std::string> lines; + + /** File size in bytes. */ + unsigned long totalSize; + + public: + /** Initializes a new file reader. */ + FileReader() : totalSize(0) { } + + /** Initializes a new file reader and reads the specified file. + * @param filename The file to read into memory. + */ + FileReader(const std::string& filename); + + /** Loads a text file from disk. + * @param filename The file to read into memory. + * @throw CoreException The file can not be loaded. + */ + void Load(const std::string& filename); + + /** Retrieves the entire contents of the file cache as a single string. */ + std::string GetString() const; + + /** Retrieves the entire contents of the file cache as a vector of strings. */ + const std::vector<std::string>& GetVector() const { return lines; } + + /** Retrieves the total size in bytes of the file. */ + unsigned long TotalSize() const { return totalSize; } +}; + +/** Implements methods for file system access */ +class CoreExport FileSystem +{ +private: + FileSystem() { } + +public: + /** Expands a path fragment to a full path. + * @param base The base path to expand from + * @param fragment The path fragment to expand on top of base. + */ + static std::string ExpandPath(const std::string& base, const std::string& fragment); + + /** + * Checks whether a file with the specified name exists on the filesystem. + * @param path The path to a file. + * @return True if the file exists; otherwise, false. + */ + static bool FileExists(const std::string& path); + + /** Gets the file name segment of a path. + * @param path The path to extract the file name from. + * @return The file name segment of a path. + */ + static std::string GetFileName(const std::string& path); + + /** Determines whether the given path starts with a Windows drive letter. + * @param path The path to validate. + * @returns True if the path begins with a Windows drive letter; otherwise, false. + */ + static bool StartsWithWindowsDriveLetter(const std::string& path); +}; diff --git a/include/flat_map.h b/include/flat_map.h new file mode 100644 index 000000000..bef1404e4 --- /dev/null +++ b/include/flat_map.h @@ -0,0 +1,383 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com> + * + * 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/>. + */ + + +#pragma once + +#include <vector> + +namespace insp +{ + +namespace detail +{ + +template <typename T, typename Comp> +class map_pair_compare : public Comp +{ + typedef T value_type; + typedef typename value_type::first_type key_type; + + public: + bool operator()(const value_type& x, const value_type& y) const + { + return Comp::operator()(x.first, y.first); + } + + bool operator()(const value_type& x, const key_type& y) const + { + return Comp::operator()(x.first, y); + } + + bool operator()(const key_type& x, const value_type& y) const + { + return Comp::operator()(x, y.first); + } +}; + +template <typename Val, typename Comp> +class map_value_compare : public std::binary_function<Val, Val, bool> +{ + public: + // Constructor should be private + + bool operator()(const Val& x, const Val& y) const + { + Comp c; + return c(x.first, y.first); + } +}; + +template <typename T, typename Comp, typename Key = T, typename ElementComp = Comp> +class flat_map_base +{ + protected: + typedef std::vector<T> storage_type; + storage_type vect; + + public: + typedef typename storage_type::iterator iterator; + typedef typename storage_type::const_iterator const_iterator; + typedef typename storage_type::reverse_iterator reverse_iterator; + typedef typename storage_type::const_reverse_iterator const_reverse_iterator; + + typedef typename storage_type::size_type size_type; + typedef typename storage_type::difference_type difference_type; + typedef Key key_type; + typedef T value_type; + + typedef Comp key_compare; + typedef ElementComp value_compare; + + flat_map_base() { } + + flat_map_base(const flat_map_base& other) + : vect(other.vect) + { + } + + size_type size() const { return vect.size(); } + bool empty() const { return vect.empty(); } + size_type capacity() const { return vect.capacity(); } + size_type max_size() const { return vect.max_size(); } + + void clear() { vect.clear(); } + void reserve(size_type n) { vect.reserve(n); } + + iterator begin() { return vect.begin(); } + iterator end() { return vect.end(); } + reverse_iterator rbegin() { return vect.rbegin(); } + reverse_iterator rend() { return vect.rend(); } + + const_iterator begin() const { return vect.begin(); } + const_iterator end() const { return vect.end(); } + const_reverse_iterator rbegin() const { return vect.rbegin(); } + const_reverse_iterator rend() const { return vect.rend(); } + + key_compare key_comp() const { return Comp(); } + + iterator erase(iterator it) { return vect.erase(it); } + iterator erase(iterator first, iterator last) { return vect.erase(first, last); } + size_type erase(const key_type& x) + { + size_type n = vect.size(); + std::pair<iterator, iterator> itpair = equal_range(x); + vect.erase(itpair.first, itpair.second); + return n - vect.size(); + } + + iterator find(const key_type& x) + { + value_compare c; + iterator it = std::lower_bound(vect.begin(), vect.end(), x, c); + if ((it != vect.end()) && (!c(x, *it))) + return it; + return vect.end(); + } + + const_iterator find(const key_type& x) const + { + // Same as above but this time we return a const_iterator + value_compare c; + const_iterator it = std::lower_bound(vect.begin(), vect.end(), x, c); + if ((it != vect.end()) && (!c(x, *it))) + return it; + return vect.end(); + } + + std::pair<iterator, iterator> equal_range(const key_type& x) + { + return std::equal_range(vect.begin(), vect.end(), x, value_compare()); + } + + std::pair<const_iterator, const_iterator> equal_range(const key_type& x) const + { + return std::equal_range(vect.begin(), vect.end(), x, value_compare()); + } + + iterator lower_bound(const key_type& x) + { + return std::lower_bound(vect.begin(), vect.end(), x, value_compare()); + } + + const_iterator lower_bound(const key_type& x) const + { + return std::lower_bound(vect.begin(), vect.end(), x, value_compare()); + } + + iterator upper_bound(const key_type& x) + { + return std::upper_bound(vect.begin(), vect.end(), x, value_compare()); + } + + const_iterator upper_bound(const key_type& x) const + { + return std::upper_bound(vect.begin(), vect.end(), x, value_compare()); + } + + size_type count(const key_type& x) const + { + std::pair<const_iterator, const_iterator> itpair = equal_range(x); + return std::distance(itpair.first, itpair.second); + } + + protected: + std::pair<iterator, bool> insert_single(const value_type& x) + { + bool inserted = false; + + value_compare c; + iterator it = std::lower_bound(vect.begin(), vect.end(), x, c); + if ((it == vect.end()) || (c(x, *it))) + { + inserted = true; + it = vect.insert(it, x); + } + return std::make_pair(it, inserted); + } + + iterator insert_multi(const value_type& x) + { + iterator it = std::lower_bound(vect.begin(), vect.end(), x, value_compare()); + return vect.insert(it, x); + } +}; + +} // namespace detail + +template <typename T, typename Comp = std::less<T> > +class flat_set : public detail::flat_map_base<T, Comp> +{ + typedef detail::flat_map_base<T, Comp> base_t; + + public: + typedef typename base_t::iterator iterator; + typedef typename base_t::value_type value_type; + + flat_set() { } + + template <typename InputIterator> + flat_set(InputIterator first, InputIterator last) + { + this->insert(first, last); + } + + flat_set(const flat_set& other) + : base_t(other) + { + } + + std::pair<iterator, bool> insert(const value_type& x) + { + return this->insert_single(x); + } + + template <typename InputIterator> + void insert(InputIterator first, InputIterator last) + { + for (; first != last; ++first) + this->insert_single(*first); + } + + void swap(flat_set& other) + { + base_t::vect.swap(other.vect); + } +}; + +template <typename T, typename Comp = std::less<T> > +class flat_multiset : public detail::flat_map_base<T, Comp> +{ + typedef detail::flat_map_base<T, Comp> base_t; + + public: + typedef typename base_t::iterator iterator; + typedef typename base_t::value_type value_type; + + flat_multiset() { } + + template <typename InputIterator> + flat_multiset(InputIterator first, InputIterator last) + { + this->insert(first, last); + } + + flat_multiset(const flat_multiset& other) + : base_t(other) + { + } + + iterator insert(const value_type& x) + { + return this->insert_multi(x); + } + + template <typename InputIterator> + void insert(InputIterator first, InputIterator last) + { + for (; first != last; ++first) + insert_multi(*first); + } + + void swap(flat_multiset& other) + { + base_t::vect.swap(other.vect); + } +}; + +template <typename T, typename U, typename Comp = std::less<T> > +class flat_map : public detail::flat_map_base<std::pair<T, U>, Comp, T, detail::map_pair_compare<std::pair<T, U>, Comp> > +{ + typedef detail::flat_map_base<std::pair<T, U>, Comp, T, detail::map_pair_compare<std::pair<T, U>, Comp> > base_t; + + public: + typedef typename base_t::iterator iterator; + typedef typename base_t::key_type key_type; + typedef typename base_t::value_type value_type; + typedef U mapped_type; + typedef typename base_t::value_compare value_compare; + + flat_map() { } + + template <typename InputIterator> + flat_map(InputIterator first, InputIterator last) + { + insert(first, last); + } + + flat_map(const flat_map& other) + : base_t(other) + { + } + + std::pair<iterator, bool> insert(const value_type& x) + { + return this->insert_single(x); + } + + template <typename InputIterator> + void insert(InputIterator first, InputIterator last) + { + for (; first != last; ++first) + this->insert_single(*first); + } + + void swap(flat_map& other) + { + base_t::vect.swap(other.vect); + } + + mapped_type& operator[](const key_type& x) + { + return insert(std::make_pair(x, mapped_type())).first->second; + } + + value_compare value_comp() const + { + return value_compare(); + } +}; + +template <typename T, typename U, typename Comp = std::less<T> > +class flat_multimap : public detail::flat_map_base<std::pair<T, U>, Comp, T, detail::map_pair_compare<std::pair<T, U>, Comp> > +{ + typedef detail::flat_map_base<std::pair<T, U>, Comp, T, detail::map_pair_compare<std::pair<T, U>, Comp> > base_t; + + public: + typedef typename base_t::iterator iterator; + typedef typename base_t::value_type value_type; + typedef U mapped_type; + typedef typename base_t::value_compare value_compare; + + flat_multimap() { } + + template <typename InputIterator> + flat_multimap(InputIterator first, InputIterator last) + { + this->insert(first, last); + } + + flat_multimap(const flat_multimap& other) + : base_t(other) + { + } + + iterator insert(const value_type& x) + { + return this->insert_multi(x); + } + + template <typename InputIterator> + void insert(InputIterator first, InputIterator last) + { + for (; first != last; ++first) + this->insert_multi(*first); + } + + void swap(flat_multimap& other) + { + base_t::vect.swap(other.vect); + } + + value_compare value_comp() const + { + return value_compare(); + } +}; + +} // namespace insp diff --git a/include/hash_map.h b/include/hash_map.h deleted file mode 100644 index e789ea66a..000000000 --- a/include/hash_map.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * InspIRCd -- Internet Relay Chat Daemon - * - * Copyright (C) 2009 Robin Burchell <robin+git@viroteck.net> - * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc> - * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org> - * Copyright (C) 2006 Oliver Lupton <oliverlupton@gmail.com> - * - * 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/>. - */ - - -#ifndef INSPIRCD_HASHMAP_H -#define INSPIRCD_HASHMAP_H - - /** Where hash_map is varies from compiler to compiler - * as it is not standard unless we have tr1. - * - * TODO: in 2.2 if we drop support for libstdc++ older than 3.4.7 and GCC older - * than 4.1 this can be cleaned up massively. - */ - #if !defined _LIBCPP_VERSION && !defined _WIN32 - #if !defined __GLIBCXX__ || __GLIBCXX__ > 20060309 - // GCC4+ has deprecated hash_map and uses tr1. But of course, uses a different include to MSVC. FOR FUCKS SAKE. - #include <tr1/unordered_map> - #define HAS_TR1_UNORDERED - #define HASHMAP_DEPRECATED - #define hash_map unordered_map - #define nspace std::tr1 - #define BEGIN_HASHMAP_NAMESPACE namespace std { namespace tr1 { - #define END_HASHMAP_NAMESPACE } } - #else - #include <ext/hash_map> - /** Oddball linux namespace for hash_map */ - #define nspace __gnu_cxx - #define BEGIN_HASHMAP_NAMESPACE namespace nspace { - #define END_HASHMAP_NAMESPACE } - #endif - #else - #include <unordered_map> - #define HAS_TR1_UNORDERED - #define HASHMAP_DEPRECATED - #define hash_map unordered_map - #define nspace std - #define BEGIN_HASHMAP_NAMESPACE namespace std { - #define END_HASHMAP_NAMESPACE } - #endif - -#endif diff --git a/include/hashcomp.h b/include/hashcomp.h index 78d7ee878..b9c568135 100644 --- a/include/hashcomp.h +++ b/include/hashcomp.h @@ -22,8 +22,7 @@ */ -#ifndef HASHCOMP_H -#define HASHCOMP_H +#pragma once #include <cstring> #include <string> @@ -31,7 +30,7 @@ #include <deque> #include <map> #include <set> -#include "hash_map.h" +#include "inspircd.h" /******************************************************* * This file contains classes and templates that deal @@ -41,11 +40,9 @@ * treat [ identical to {, ] identical to }, and \ * as identical to |. * - * Our hashing functions are designed to accept - * std::string and compare/hash them as type irc::string - * by converting them internally. This makes them - * backwards compatible with other code which is not - * aware of irc::string. + * There are functors that accept std::string and + * compare/hash them as type irc::string by using + * mapping arrays internally. *******************************************************/ /** Seperate from the other casemap tables so that code *can* still exclusively rely on RFC casemapping @@ -67,340 +64,90 @@ CoreExport extern unsigned const char rfc_case_insensitive_map[256]; */ CoreExport extern unsigned const char ascii_case_insensitive_map[256]; -/** Case sensitive (identity) map. - */ -CoreExport extern unsigned const char rfc_case_sensitive_map[256]; - -template<typename T> const T& SearchAndReplace(T& text, const T& pattern, const T& replace) -{ - T replacement; - if ((!pattern.empty()) && (!text.empty())) - { - for (std::string::size_type n = 0; n != text.length(); ++n) - { - if (text.length() >= pattern.length() && text.substr(n, pattern.length()) == pattern) - { - /* Found the pattern in the text, replace it, and advance */ - replacement.append(replace); - n = n + pattern.length() - 1; - } - else - { - replacement += text[n]; - } - } - } - text = replacement; - return text; -} - /** The irc namespace contains a number of helper classes. */ namespace irc { + /** Check if two IRC object (e.g. nick or channel) names are equal. + * This function uses national_case_insensitive_map to determine equality, which, by default does comparison + * according to RFC 1459, treating certain otherwise non-identical characters as identical. + * @param s1 First string to compare + * @param s2 Second string to compare + * @return True if the two names are equal, false otherwise + */ + CoreExport bool equals(const std::string& s1, const std::string& s2); + + /** Check whether \p needle exists within \p haystack. + * @param haystack The string to search within. + * @param needle The string to search for. + * @return Either the index at which \p needle was found or std::string::npos. + */ + CoreExport size_t find(const std::string& haystack, const std::string& needle); /** This class returns true if two strings match. * Case sensitivity is ignored, and the RFC 'character set' * is adhered to */ - struct CoreExport StrHashComp + struct StrHashComp { /** The operator () does the actual comparison in hash_map */ - bool operator()(const std::string& s1, const std::string& s2) const; - }; - - /** The irc_char_traits class is used for RFC-style comparison of strings. - * This class is used to implement irc::string, a case-insensitive, RFC- - * comparing string class. - */ - struct irc_char_traits : std::char_traits<char> { - - /** Check if two chars match. - * @param c1st First character - * @param c2nd Second character - * @return true if the characters are equal - */ - static bool eq(char c1st, char c2nd); - - /** Check if two chars do NOT match. - * @param c1st First character - * @param c2nd Second character - * @return true if the characters are unequal - */ - static bool ne(char c1st, char c2nd); - - /** Check if one char is less than another. - * @param c1st First character - * @param c2nd Second character - * @return true if c1st is less than c2nd - */ - static bool lt(char c1st, char c2nd); - - /** Compare two strings of size n. - * @param str1 First string - * @param str2 Second string - * @param n Length to compare to - * @return similar to strcmp, zero for equal, less than zero for str1 - * being less and greater than zero for str1 being greater than str2. - */ - static CoreExport int compare(const char* str1, const char* str2, size_t n); - - /** Find a char within a string up to position n. - * @param s1 String to find in - * @param n Position to search up to - * @param c Character to search for - * @return Pointer to the first occurance of c in s1 - */ - static CoreExport const char* find(const char* s1, int n, char c); - }; - - /** Compose a hex string from raw data. - * @param raw The raw data to compose hex from - * @param rawsz The size of the raw data buffer - * @return The hex string. - */ - CoreExport std::string hex(const unsigned char *raw, size_t rawsz); - - /** This typedef declares irc::string based upon irc_char_traits. - */ - typedef std::basic_string<char, irc_char_traits, std::allocator<char> > string; - - /** irc::stringjoiner joins string lists into a string, using - * the given seperator string. - * This class can join a vector of std::string, a deque of - * std::string, or a const char* const* array, using overloaded - * constructors. - */ - class CoreExport stringjoiner - { - private: - - /** Output string - */ - std::string joined; - - public: - - /** Join elements of a vector, between (and including) begin and end - * @param seperator The string to seperate values with - * @param sequence One or more items to seperate - * @param begin The starting element in the sequence to be joined - * @param end The ending element in the sequence to be joined - */ - stringjoiner(const std::string &seperator, const std::vector<std::string> &sequence, int begin, int end); - - /** Join elements of a deque, between (and including) begin and end - * @param seperator The string to seperate values with - * @param sequence One or more items to seperate - * @param begin The starting element in the sequence to be joined - * @param end The ending element in the sequence to be joined - */ - stringjoiner(const std::string &seperator, const std::deque<std::string> &sequence, int begin, int end); - - /** Join elements of an array of char arrays, between (and including) begin and end - * @param seperator The string to seperate values with - * @param sequence One or more items to seperate - * @param begin The starting element in the sequence to be joined - * @param end The ending element in the sequence to be joined - */ - stringjoiner(const std::string &seperator, const char* const* sequence, int begin, int end); - - /** Get the joined sequence - * @return A reference to the joined string - */ - std::string& GetJoined(); + bool operator()(const std::string& s1, const std::string& s2) const + { + return equals(s1, s2); + } }; - /** irc::modestacker stacks mode sequences into a list. - * It can then reproduce this list, clamped to a maximum of MAXMODES - * values per line. - */ - class CoreExport modestacker + struct insensitive { - private: - /** The mode sequence and its parameters - */ - std::deque<std::string> sequence; - - /** True if the mode sequence is initially adding - * characters, false if it is initially removing - * them - */ - bool adding; - public: - - /** Construct a new modestacker. - * @param add True if the stack is adding modes, - * false if it is removing them - */ - modestacker(bool add); - - /** Push a modeletter and its parameter onto the stack. - * No checking is performed as to if this mode actually - * requires a parameter. If you stack invalid mode - * sequences, they will be tidied if and when they are - * passed to a mode parser. - * @param modeletter The mode letter to insert - * @param parameter The parameter for the mode - */ - void Push(char modeletter, const std::string ¶meter); - - /** Push a modeletter without parameter onto the stack. - * No checking is performed as to if this mode actually - * requires a parameter. If you stack invalid mode - * sequences, they will be tidied if and when they are - * passed to a mode parser. - * @param modeletter The mode letter to insert - */ - void Push(char modeletter); - - /** Push a '+' symbol onto the stack. - */ - void PushPlus(); - - /** Push a '-' symbol onto the stack. - */ - void PushMinus(); - - /** Return zero or more elements which form the - * mode line. This will be clamped to a max of - * MAXMODES items (MAXMODES-1 mode parameters and - * one mode sequence string), and max_line_size - * characters. As specified below, this function - * should be called in a loop until it returns zero, - * indicating there are no more modes to return. - * @param result The vector to populate. This will not - * be cleared before it is used. - * @param max_line_size The maximum size of the line - * to build, in characters, seperate to MAXMODES. - * @return The number of elements in the deque. - * The function should be called repeatedly until it - * returns 0, in case there are multiple lines of - * mode changes to be obtained. - */ - int GetStackedLine(std::vector<std::string> &result, int max_line_size = 360); - - /** deprecated compatability interface - TODO remove */ - int GetStackedLine(std::deque<std::string> &result, int max_line_size = 360) { - std::vector<std::string> r; - int n = GetStackedLine(r, max_line_size); - result.clear(); - result.insert(result.end(), r.begin(), r.end()); - return n; - } + size_t CoreExport operator()(const std::string &s) const; }; - /** irc::tokenstream reads a string formatted as per RFC1459 and RFC2812. - * It will split the string into 'tokens' each containing one parameter - * from the string. - * For instance, if it is instantiated with the string: - * "PRIVMSG #test :foo bar baz qux" - * then each successive call to tokenstream::GetToken() will return - * "PRIVMSG", "#test", "foo bar baz qux", "". - * Note that if the whole string starts with a colon this is not taken - * to mean the string is all one parameter, and the first item in the - * list will be ":item". This is to allow for parsing 'source' fields - * from data. - */ - class CoreExport tokenstream + struct insensitive_swo { - private: - - /** Original string - */ - std::string tokens; - - /** Last position of a seperator token - */ - std::string::iterator last_starting_position; - - /** Current string position - */ - std::string::iterator n; - - /** True if the last value was an ending value - */ - bool last_pushed; - public: - - /** Create a tokenstream and fill it with the provided data - */ - tokenstream(const std::string &source); - - /** Destructor - */ - ~tokenstream(); - - /** Fetch the next token from the stream as a std::string - * @param token The next token available, or an empty string if none remain - * @return True if tokens are left to be read, false if the last token was just retrieved. - */ - bool GetToken(std::string &token); - - /** Fetch the next token from the stream as an irc::string - * @param token The next token available, or an empty string if none remain - * @return True if tokens are left to be read, false if the last token was just retrieved. - */ - bool GetToken(irc::string &token); - - /** Fetch the next token from the stream as an integer - * @param token The next token available, or undefined if none remain - * @return True if tokens are left to be read, false if the last token was just retrieved. - */ - bool GetToken(int &token); - - /** Fetch the next token from the stream as a long integer - * @param token The next token available, or undefined if none remain - * @return True if tokens are left to be read, false if the last token was just retrieved. - */ - bool GetToken(long &token); + bool CoreExport operator()(const std::string& a, const std::string& b) const; }; /** irc::sepstream allows for splitting token seperated lists. * Each successive call to sepstream::GetToken() returns * the next token, until none remain, at which point the method returns - * an empty string. + * false. */ class CoreExport sepstream { - private: + protected: /** Original string. */ std::string tokens; - /** Last position of a seperator token + /** Separator value */ - std::string::iterator last_starting_position; + char sep; /** Current string position */ - std::string::iterator n; - /** Seperator value + size_t pos; + /** If set then GetToken() can return an empty string */ - char sep; + bool allow_empty; public: /** Create a sepstream and fill it with the provided data */ - sepstream(const std::string &source, char seperator); - - /** Destructor - */ - virtual ~sepstream(); + sepstream(const std::string &source, char separator, bool allowempty = false); /** Fetch the next token from the stream * @param token The next token from the stream is placed here * @return True if tokens still remain, false if there are none left */ - virtual bool GetToken(std::string &token); + bool GetToken(std::string& token); /** Fetch the entire remaining stream, without tokenizing * @return The remaining part of the stream */ - virtual const std::string GetRemaining(); + const std::string GetRemaining(); /** Returns true if the end of the stream has been reached * @return True if the end of the stream has been reached, otherwise false */ - virtual bool StreamEnd(); + bool StreamEnd(); }; /** A derived form of sepstream, which seperates on commas @@ -408,9 +155,9 @@ namespace irc class CoreExport commasepstream : public sepstream { public: - /** Initialize with comma seperator + /** Initialize with comma separator */ - commasepstream(const std::string &source) : sepstream(source, ',') + commasepstream(const std::string &source, bool allowempty = false) : sepstream(source, ',', allowempty) { } }; @@ -420,13 +167,51 @@ namespace irc class CoreExport spacesepstream : public sepstream { public: - /** Initialize with space seperator + /** Initialize with space separator */ - spacesepstream(const std::string &source) : sepstream(source, ' ') + spacesepstream(const std::string &source, bool allowempty = false) : sepstream(source, ' ', allowempty) { } }; + /** irc::tokenstream reads a string formatted as per RFC1459 and RFC2812. + * It will split the string into 'tokens' each containing one parameter + * from the string. + * For instance, if it is instantiated with the string: + * "PRIVMSG #test :foo bar baz qux" + * then each successive call to tokenstream::GetToken() will return + * "PRIVMSG", "#test", "foo bar baz qux", "". + * Note that if the whole string starts with a colon this is not taken + * to mean the string is all one parameter, and the first item in the + * list will be ":item". This is to allow for parsing 'source' fields + * from data. + */ + class CoreExport tokenstream + { + private: + /** The message we are parsing tokens from. */ + std::string message; + + /** The current position within the message. */ + size_t position; + + public: + /** Create a tokenstream and fill it with the provided data. */ + tokenstream(const std::string& msg, size_t start = 0); + + /** Retrieve the next <middle> token in the token stream. + * @param token The next token available, or an empty string if none remain. + * @return True if tokens are left to be read, false if the last token was just retrieved. + */ + bool GetMiddle(std::string& token); + + /** Retrieve the next <trailing> token in the token stream. + * @param token The next token available, or an empty string if none remain. + * @return True if tokens are left to be read, false if the last token was just retrieved. + */ + bool GetTrailing(std::string& token); + }; + /** The portparser class seperates out a port range into integers. * A port range may be specified in the input string in the form * "6660,6661,6662-6669,7020". The end of the stream is indicated by @@ -480,182 +265,4 @@ namespace irc */ long GetToken(); }; - - /** Turn _ characters in a string into spaces - * @param n String to translate - * @return The new value with _ translated to space. - */ - CoreExport const char* Spacify(const char* n); - - struct hash - { - /** Hash an irc::string using RFC1459 case sensitivity rules - * @param s A string to hash - * @return The hash value - */ - size_t CoreExport operator()(const irc::string &s) const; - }; -} - -/* Define operators for using >> and << with irc::string to an ostream on an istream. */ -/* This was endless fun. No. Really. */ -/* It was also the first core change Ommeh made, if anyone cares */ - -/** Operator << for irc::string - */ -inline std::ostream& operator<<(std::ostream &os, const irc::string &str) { return os << str.c_str(); } - -/** Operator >> for irc::string - */ -inline std::istream& operator>>(std::istream &is, irc::string &str) -{ - std::string tmp; - is >> tmp; - str = tmp.c_str(); - return is; -} - -/* Define operators for + and == with irc::string to std::string for easy assignment - * and comparison - * - * Operator + - */ -inline std::string operator+ (std::string& leftval, irc::string& rightval) -{ - return leftval + std::string(rightval.c_str()); } - -/* Define operators for + and == with irc::string to std::string for easy assignment - * and comparison - * - * Operator + - */ -inline irc::string operator+ (irc::string& leftval, std::string& rightval) -{ - return leftval + irc::string(rightval.c_str()); -} - -/* Define operators for + and == with irc::string to std::string for easy assignment - * and comparison - * - * Operator == - */ -inline bool operator== (const std::string& leftval, const irc::string& rightval) -{ - return (leftval.c_str() == rightval); -} - -/* Define operators for + and == with irc::string to std::string for easy assignment - * and comparison - * - * Operator == - */ -inline bool operator== (const irc::string& leftval, const std::string& rightval) -{ - return (leftval == rightval.c_str()); -} - -/* Define operators != for irc::string to std::string for easy comparison - */ -inline bool operator!= (const irc::string& leftval, const std::string& rightval) -{ - return !(leftval == rightval.c_str()); -} - -/* Define operators != for std::string to irc::string for easy comparison - */ -inline bool operator!= (const std::string& leftval, const irc::string& rightval) -{ - return !(leftval.c_str() == rightval); -} - -/** Assign an irc::string to a std::string. - */ -inline std::string assign(const irc::string &other) { return other.c_str(); } - -/** Assign a std::string to an irc::string. - */ -inline irc::string assign(const std::string &other) { return other.c_str(); } - -/** Trim the leading and trailing spaces from a std::string. - */ -inline std::string& trim(std::string &str) -{ - std::string::size_type start = str.find_first_not_of(" "); - std::string::size_type end = str.find_last_not_of(" "); - if (start == std::string::npos || end == std::string::npos) - str = ""; - else - str = str.substr(start, end-start+1); - - return str; -} - -/** Hashing stuff is totally different on vc++'s hash_map implementation, so to save a buttload of - * \#ifdefs we'll just do it all at once. Except, of course, with TR1, when it's the same as GCC. - */ -BEGIN_HASHMAP_NAMESPACE - - /** Hashing function to hash irc::string - */ -#if defined(_WIN32) && !defined(HAS_TR1_UNORDERED) - template<> class CoreExport hash_compare<irc::string, std::less<irc::string> > - { - public: - enum { bucket_size = 4, min_buckets = 8 }; /* Got these numbers from the CRT source, if anyone wants to change them feel free. */ - - /** Compare two irc::string values for hashing in hash_map - */ - bool operator()(const irc::string & s1, const irc::string & s2) const - { - if(s1.length() != s2.length()) return true; - return (irc::irc_char_traits::compare(s1.c_str(), s2.c_str(), (size_t)s1.length()) < 0); - } - - /** Hash an irc::string value for hash_map - */ - size_t operator()(const irc::string & s) const; - }; - - template<> class CoreExport hash_compare<std::string, std::less<std::string> > - { - public: - enum { bucket_size = 4, min_buckets = 8 }; /* Again, from the CRT source */ - - /** Compare two std::string values for hashing in hash_map - */ - bool operator()(const std::string & s1, const std::string & s2) const - { - if(s1.length() != s2.length()) return true; - return (irc::irc_char_traits::compare(s1.c_str(), s2.c_str(), (size_t)s1.length()) < 0); - } - - /** Hash a std::string using RFC1459 case sensitivity rules - * @param s A string to hash - * @return The hash value - */ - size_t operator()(const std::string & s) const; - }; -#else - - /* XXX FIXME: Implement a hash function overriding std::string's that works with TR1! */ - -#ifdef HASHMAP_DEPRECATED - struct insensitive -#else - CoreExport template<> struct hash<std::string> -#endif - { - size_t CoreExport operator()(const std::string &s) const; - }; - -#endif - - /** Convert a string to lower case respecting RFC1459 - * @param n A string to lowercase - */ - void strlower(char *n); - -END_HASHMAP_NAMESPACE - -#endif diff --git a/include/inspircd.h b/include/inspircd.h index 78348ed54..934f2f82b 100644 --- a/include/inspircd.h +++ b/include/inspircd.h @@ -23,63 +23,60 @@ */ -#ifndef INSPIRCD_H -#define INSPIRCD_H +#pragma once -#define _FILE_OFFSET_BITS 64 -#ifndef _LARGEFILE_SOURCE -#define _LARGEFILE_SOURCE -#endif - -#ifndef _WIN32 -#define DllExport -#define CoreExport -#else -#include "inspircd_win32wrapper.h" -/** Windows defines these already */ -#undef ERROR -#endif - -#ifdef __GNUC__ -#define CUSTOM_PRINTF(STRING, FIRST) __attribute__((format(printf, STRING, FIRST))) -#else -#define CUSTOM_PRINTF(STRING, FIRST) -#endif - -// Required system headers. +#include <cfloat> +#include <climits> +#include <cmath> #include <csignal> -#include <ctime> #include <cstdarg> -#include <algorithm> -#include <cmath> -#include <cstring> -#include <climits> #include <cstdio> -#ifndef _WIN32 -#include <unistd.h> -#endif +#include <cstring> +#include <ctime> +#include <stdint.h> -#include <sstream> -#include <string> -#include <vector> -#include <list> +#include <algorithm> +#include <bitset> #include <deque> +#include <list> #include <map> -#include <bitset> #include <set> -#include <time.h> -#include "inspircd_config.h" -#include "inspircd_version.h" +#include <sstream> +#include <string> +#include <vector> + +#include "intrusive_list.h" +#include "flat_map.h" +#include "compat.h" +#include "aligned_storage.h" #include "typedefs.h" -#include "consolecolors.h" +#include "convto.h" +#include "stdalgo.h" CoreExport extern InspIRCd* ServerInstance; -#include "caller.h" +/** Base class for manager classes that are still accessed using -> but are no longer pointers + */ +template <typename T> +struct fakederef +{ + T* operator->() + { + return static_cast<T*>(this); + } +}; + +#include "config.h" +#include "dynref.h" +#include "consolecolors.h" #include "cull_list.h" #include "extensible.h" +#include "fileutils.h" +#include "ctables.h" #include "numerics.h" +#include "numeric.h" #include "uid.h" +#include "server.h" #include "users.h" #include "channels.h" #include "timer.h" @@ -87,110 +84,20 @@ CoreExport extern InspIRCd* ServerInstance; #include "logger.h" #include "usermanager.h" #include "socket.h" -#include "ctables.h" #include "command_parse.h" #include "mode.h" #include "socketengine.h" #include "snomasks.h" #include "filelogger.h" +#include "message.h" #include "modules.h" +#include "clientprotocol.h" #include "threadengine.h" #include "configreader.h" #include "inspstring.h" #include "protocol.h" - -#ifndef PATH_MAX -#warning Potentially broken system, PATH_MAX undefined -#define PATH_MAX 4096 -#endif - -/** - * Used to define the maximum number of parameters a command may have. - */ -#define MAXPARAMETERS 127 - -/** Returned by some functions to indicate failure. - */ -#define ERROR -1 - -/** Support for librodent - - * see http://www.chatspike.net/index.php?z=64 - */ -#define ETIREDHAMSTERS EAGAIN - -/** Template function to convert any input type to std::string - */ -template<typename T> inline std::string ConvNumeric(const T &in) -{ - if (in == 0) return "0"; - char res[MAXBUF]; - char* out = res; - T quotient = in; - while (quotient) { - *out = "0123456789"[ std::abs( (long)quotient % 10 ) ]; - ++out; - quotient /= 10; - } - if (in < 0) - *out++ = '-'; - *out = 0; - std::reverse(res,out); - return res; -} - -/** Template function to convert any input type to std::string - */ -inline std::string ConvToStr(const int in) -{ - return ConvNumeric(in); -} - -/** Template function to convert any input type to std::string - */ -inline std::string ConvToStr(const long in) -{ - return ConvNumeric(in); -} - -/** Template function to convert any input type to std::string - */ -inline std::string ConvToStr(const char* in) -{ - return in; -} - -/** Template function to convert any input type to std::string - */ -inline std::string ConvToStr(const bool in) -{ - return (in ? "1" : "0"); -} - -/** Template function to convert any input type to std::string - */ -inline std::string ConvToStr(char in) -{ - return std::string(1, in); -} - -/** Template function to convert any input type to std::string - */ -template <class T> inline std::string ConvToStr(const T &in) -{ - std::stringstream tmp; - if (!(tmp << in)) return std::string(); - return tmp.str(); -} - -/** Template function to convert any input type to any other type - * (usually an integer or numeric type) - */ -template<typename T> inline long ConvToInt(const T &in) -{ - std::stringstream tmp; - if (!(tmp << in)) return 0; - return atol(tmp.str().c_str()); -} +#include "bancache.h" +#include "isupportmanager.h" /** This class contains various STATS counters * It is used by the InspIRCd class, which internally @@ -201,38 +108,38 @@ class serverstats public: /** Number of accepted connections */ - unsigned long statsAccept; + unsigned long Accept; /** Number of failed accepts */ - unsigned long statsRefused; + unsigned long Refused; /** Number of unknown commands seen */ - unsigned long statsUnknown; + unsigned long Unknown; /** Number of nickname collisions handled */ - unsigned long statsCollisions; + unsigned long Collisions; /** Number of DNS queries sent out */ - unsigned long statsDns; + unsigned long Dns; /** Number of good DNS replies received * NOTE: This may not tally to the number sent out, * due to timeouts and other latency issues. */ - unsigned long statsDnsGood; + unsigned long DnsGood; /** Number of bad (negative) DNS replies received * NOTE: This may not tally to the number sent out, * due to timeouts and other latency issues. */ - unsigned long statsDnsBad; + unsigned long DnsBad; /** Number of inbound connections seen */ - unsigned long statsConnects; + unsigned long Connects; /** Total bytes of data transmitted */ - unsigned long statsSent; + unsigned long Sent; /** Total bytes of data received */ - unsigned long statsRecv; + unsigned long Recv; #ifdef _WIN32 /** Cpu usage at last sample */ @@ -254,23 +161,12 @@ class serverstats /** The constructor initializes all the counts to zero */ serverstats() - : statsAccept(0), statsRefused(0), statsUnknown(0), statsCollisions(0), statsDns(0), - statsDnsGood(0), statsDnsBad(0), statsConnects(0), statsSent(0), statsRecv(0) + : Accept(0), Refused(0), Unknown(0), Collisions(0), Dns(0), + DnsGood(0), DnsBad(0), Connects(0), Sent(0), Recv(0) { } }; -DEFINE_HANDLER2(IsNickHandler, bool, const char*, size_t); -DEFINE_HANDLER2(GenRandomHandler, void, char*, size_t); -DEFINE_HANDLER1(IsIdentHandler, bool, const char*); -DEFINE_HANDLER1(FloodQuitUserHandler, void, User*); -DEFINE_HANDLER2(IsChannelHandler, bool, const char*, size_t); -DEFINE_HANDLER1(IsSIDHandler, bool, const std::string&); -DEFINE_HANDLER1(RehashHandler, void, const std::string&); -DEFINE_HANDLER3(OnCheckExemptionHandler, ModResult, User*, Channel*, const std::string&); - -class TestSuite; - /** The main class of the irc server. * This class contains instances of all the other classes in this software. * Amongst other things, it contains a ModeParser, a DNS object, a CommandParser @@ -280,10 +176,6 @@ class TestSuite; class CoreExport InspIRCd { private: - /** Holds the current UID. Used to generate the next one. - */ - char current_uid[UUID_LENGTH]; - /** Set up the signal handlers */ void SetSignals(); @@ -293,25 +185,6 @@ class CoreExport InspIRCd */ bool DaemonSeed(); - /** Iterate the list of BufferedSocket objects, removing ones which have timed out - * @param TIME the current time - */ - void DoSocketTimeouts(time_t TIME); - - /** Increments the current UID by one. - */ - void IncrementUID(int pos); - - /** Perform background user events such as PING checks - */ - void DoBackgroundUserStuff(); - - /** Returns true when all modules have done pre-registration checks on a user - * @param user The user to verify - * @return True if all modules have finished checking this user - */ - bool AllModulesReportReady(LocalUser* user); - /** The current time, updated in the mainloop */ struct timespec TIME; @@ -321,25 +194,23 @@ class CoreExport InspIRCd */ char ReadBuffer[65535]; + ClientProtocol::RFCEvents rfcevents; + + /** Check we aren't running as root, and exit if we are + * with exit code EXIT_STATUS_ROOT. + */ + void CheckRoot(); + public: + UIDGenerator UIDGen; + /** Global cull list, will be processed on next iteration */ CullList GlobalCulls; /** Actions that must happen outside of the current call stack */ ActionList AtomicActions; - /**** Functors ****/ - - IsNickHandler HandleIsNick; - IsIdentHandler HandleIsIdent; - FloodQuitUserHandler HandleFloodQuitUser; - OnCheckExemptionHandler HandleOnCheckExemption; - IsChannelHandler HandleIsChannel; - IsSIDHandler HandleIsSID; - RehashHandler HandleRehash; - GenRandomHandler HandleGenRandom; - /** Globally accessible fake user record. This is used to force mode changes etc across s2s, etc.. bit ugly, but.. better than how this was done in 1.1 * Reason for it: * kludge alert! @@ -350,28 +221,12 @@ class CoreExport InspIRCd */ FakeUser* FakeClient; - /** Returns the next available UID for this server. - */ - std::string GetUID(); - - static const char LogHeader[]; - /** Find a user in the UUID hash * @param uid The UUID to find * @return A pointer to the user, or NULL if the user does not exist */ User* FindUUID(const std::string &uid); - /** Find a user in the UUID hash - * @param uid The UUID to find - * @return A pointer to the user, or NULL if the user does not exist - */ - User* FindUUID(const char *uid); - - /** Build the ISUPPORT string by triggering all modules On005Numeric events - */ - void BuildISupport(); - /** Time this ircd was booted */ time_t startup_time; @@ -384,19 +239,15 @@ class CoreExport InspIRCd /** Mode handler, handles mode setting and removal */ - ModeParser* Modes; + ModeParser Modes; /** Command parser, handles client to server commands */ - CommandParser* Parser; - - /** Socket engine, handles socket activity events - */ - SocketEngine* SE; + CommandParser Parser; /** Thread engine, Handles threading where required */ - ThreadEngine* Threads; + ThreadEngine Threads; /** The thread/class used to read config files in REHASH and on startup */ @@ -404,21 +255,21 @@ class CoreExport InspIRCd /** LogManager handles logging. */ - LogManager *Logs; + LogManager Logs; /** ModuleManager contains everything related to loading/unloading * modules. */ - ModuleManager* Modules; + ModuleManager Modules; /** BanCacheManager is used to speed up checking of restrictions on connection * to the IRCd. */ - BanCacheManager *BanCache; + BanCacheManager BanCache; /** Stats class, holds miscellaneous stats counters */ - serverstats* stats; + serverstats stats; /** Server Config class, holds configuration file data */ @@ -427,15 +278,11 @@ class CoreExport InspIRCd /** Snomask manager - handles routing of snomask messages * to opers. */ - SnomaskManager* SNO; - - /** DNS class, provides resolver facilities to the core and modules - */ - DNS* Res; + SnomaskManager SNO; /** Timer manager class, triggers Timer timer events */ - TimerManager* Timers; + TimerManager Timers; /** X-Line manager. Handles G/K/Q/E line setting, removal and matching */ @@ -443,11 +290,11 @@ class CoreExport InspIRCd /** User manager. Various methods and data associated with users. */ - UserManager *Users; + UserManager Users; /** Channel list, a hash_map containing all channels XXX move to channel manager class */ - chan_hash* chanlist; + chan_hash chanlist; /** List of the open ports */ @@ -461,13 +308,12 @@ class CoreExport InspIRCd */ ProtocolInterface* PI; - /** Holds extensible for user nickforced + /** Default implementation of the ProtocolInterface, does nothing */ - LocalIntExt NICKForced; + ProtocolInterface DefaultProtocolInterface; - /** Holds extensible for user operquit - */ - LocalStringExt OperQuit; + /** Manages the generation and transmission of ISUPPORT. */ + ISupportManager ISupport; /** Get the current time * Because this only calls time() once every time around the mainloop, @@ -485,37 +331,33 @@ class CoreExport InspIRCd * @param printable if false, the string will use characters 0-255; otherwise, * it will be limited to 0x30-0x7E ('0'-'~', nonspace printable characters) */ - std::string GenRandomStr(int length, bool printable = true); + std::string GenRandomStr(unsigned int length, bool printable = true); /** Generate a random integer. * This is generally more secure than rand() */ unsigned long GenRandomInt(unsigned long max); /** Fill a buffer with random bits */ - caller2<void, char*, size_t> GenRandom; + TR1NS::function<void(char*, size_t)> GenRandom; - /** Bind all ports specified in the configuration file. - * @return The number of ports bound without error + /** Fills the output buffer with the specified number of random characters. + * This is the default function for InspIRCd::GenRandom. + * @param output The output buffer to store random characters in. + * @param max The maximum number of random characters to put in the buffer. */ - int BindPorts(FailedPortList &failed_ports); + static void DefaultGenRandom(char* output, size_t max); - /** Binds a socket on an already open file descriptor - * @param sockfd A valid file descriptor of an open socket - * @param port The port number to bind to - * @param addr The address to bind to (IP only) - * @param dolisten Should this port be listened on? - * @return True if the port was bound successfully + /** Bind to a specific port from a config tag. + * @param Tag the tag that contains bind information. + * @param sa The endpoint to listen on. + * @params old_ports Previously listening ports that may be on the same endpoint. */ - bool BindSocket(int sockfd, int port, const char* addr, bool dolisten = true); + bool BindPort(ConfigTag* tag, const irc::sockets::sockaddrs& sa, std::vector<ListenSocket*>& old_ports); - /** Gets the GECOS (description) field of the given server. - * If the servername is not that of the local server, the name - * is passed to handling modules which will attempt to determine - * the GECOS that bleongs to the given servername. - * @param servername The servername to find the description of - * @return The description of this server, or of the local server + /** Bind all ports specified in the configuration file. + * @return The number of ports bound without error */ - std::string GetServerDescription(const std::string& servername); + int BindPorts(FailedPortList &failed_ports); /** Find a user in the nick hash. * If the user cant be found in the nick hash check the uuid hash @@ -524,17 +366,6 @@ class CoreExport InspIRCd */ User* FindNick(const std::string &nick); - /** Find a user in the nick hash. - * If the user cant be found in the nick hash check the uuid hash - * @param nick The nickname to find - * @return A pointer to the user, or NULL if the user does not exist - */ - User* FindNick(const char* nick); - - /** Find a user in the nick hash ONLY - */ - User* FindNickOnly(const char* nick); - /** Find a user in the nick hash ONLY */ User* FindNickOnly(const std::string &nick); @@ -545,38 +376,31 @@ class CoreExport InspIRCd */ Channel* FindChan(const std::string &chan); - /** Find a channel in the channels hash - * @param chan The channel to find - * @return A pointer to the channel, or NULL if the channel does not exist + /** Get a hash map containing all channels, keyed by their name + * @return A hash map mapping channel names to Channel pointers */ - Channel* FindChan(const char* chan); + chan_hash& GetChans() { return chanlist; } - /** Check we aren't running as root, and exit if we are - * @return Depending on the configuration, this function may never return - */ - void CheckRoot(); + /** Determines whether an channel name is valid. */ + TR1NS::function<bool(const std::string&)> IsChannel; - /** Determine the right path for, and open, the logfile - * @param argv The argv passed to main() initially, used to calculate program path - * @param argc The argc passed to main() initially, used to calculate program path - * @return True if the log could be opened, false if otherwise - */ - bool OpenLog(char** argv, int argc); + /** Determines whether a channel name is valid according to the RFC 1459 rules. + * This is the default function for InspIRCd::IsChannel. + * @param channel The channel name to validate. + * @return True if the channel name is valid according to RFC 1459 rules; otherwise, false. + */ + static bool DefaultIsChannel(const std::string& channel); - /** Return true if a channel name is valid - * @param chname A channel name to verify - * @return True if the name is valid + /** Determines whether a hostname is valid according to RFC 5891 rules. + * @param host The hostname to validate. + * @return True if the hostname is valid; otherwise, false. */ - caller2<bool, const char*, size_t> IsChannel; + static bool IsHost(const std::string& host); /** Return true if str looks like a server ID - * @param string to check against + * @param sid string to check against */ - caller1<bool, const std::string&> IsSID; - - /** Rehash the local server - */ - caller1<void, const std::string&> Rehash; + static bool IsSID(const std::string& sid); /** Handles incoming signals after being set * @param signal the signal recieved @@ -596,75 +420,33 @@ class CoreExport InspIRCd */ void Exit(int status); - /** Causes the server to exit immediately with exit code 0. - * The status code is required for signal handlers, and ignored. - */ - static void QuickExit(int status); - - /** Return a count of channels on the network - * @return The number of channels - */ - long ChannelCount(); - - /** Send an error notice to all local users, opered and unopered - * @param s The error string to send - */ - void SendError(const std::string &s); + /** Formats the input string with the specified arguments. + * @param formatString The string to format + * @param ... A variable number of format arguments. + * @return The formatted string + */ + static std::string Format(const char* formatString, ...) CUSTOM_PRINTF(1, 2); + static std::string Format(va_list& vaList, const char* formatString) CUSTOM_PRINTF(2, 0); - /** Return true if a nickname is valid - * @param n A nickname to verify - * @return True if the nick is valid - */ - caller2<bool, const char*, size_t> IsNick; + /** Determines whether a nickname is valid. */ + TR1NS::function<bool(const std::string&)> IsNick; - /** Return true if an ident is valid - * @param An ident to verify - * @return True if the ident is valid + /** Determines whether a nickname is valid according to the RFC 1459 rules. + * This is the default function for InspIRCd::IsNick. + * @param nick The nickname to validate. + * @return True if the nickname is valid according to RFC 1459 rules; otherwise, false. */ - caller1<bool, const char*> IsIdent; + static bool DefaultIsNick(const std::string& nick); - /** Add a dns Resolver class to this server's active set - * @param r The resolver to add - * @param cached If this value is true, then the cache will - * be searched for the DNS result, immediately. If the value is - * false, then a request will be sent to the nameserver, and the - * result will not be immediately available. You should usually - * use the boolean value which you passed to the Resolver - * constructor, which Resolver will set appropriately depending - * on if cached results are available and haven't expired. It is - * however safe to force this value to false, forcing a remote DNS - * lookup, but not an update of the cache. - * @return True if the operation completed successfully. Note that - * if this method returns true, you should not attempt to access - * the resolver class you pass it after this call, as depending upon - * the request given, the object may be deleted! - */ - bool AddResolver(Resolver* r, bool cached); + /** Determines whether an ident is valid. */ + TR1NS::function<bool(const std::string&)> IsIdent; - /** Add a command to this server's command parser - * @param f A Command command handler object to add - * @throw ModuleException Will throw ModuleExcption if the command already exists - */ - inline void AddCommand(Command *f) - { - Modules->AddService(*f); - } - - /** Send a modechange. - * The parameters provided are identical to that sent to the - * handler for class cmd_mode. - * @param parameters The mode parameters - * @param user The user to send error messages to - */ - void SendMode(const std::vector<std::string>& parameters, User *user); - - /** Send a modechange and route it to the network. - * The parameters provided are identical to that sent to the - * handler for class cmd_mode. - * @param parameters The mode parameters - * @param user The user to send error messages to - */ - void SendGlobalMode(const std::vector<std::string>& parameters, User *user); + /** Determines whether a ident is valid according to the RFC 1459 rules. + * This is the default function for InspIRCd::IsIdent. + * @param ident The ident to validate. + * @return True if the ident is valid according to RFC 1459 rules; otherwise, false. + */ + static bool DefaultIsIdent(const std::string& ident); /** Match two strings using pattern matching, optionally, with a map * to check case against (may be NULL). If map is null, match will be case insensitive. @@ -672,8 +454,8 @@ class CoreExport InspIRCd * @param mask The glob pattern to match against. * @param map The character map to use when matching. */ - static bool Match(const std::string &str, const std::string &mask, unsigned const char *map = NULL); - static bool Match(const char *str, const char *mask, unsigned const char *map = NULL); + static bool Match(const std::string& str, const std::string& mask, unsigned const char* map = NULL); + static bool Match(const char* str, const char* mask, unsigned const char* map = NULL); /** Match two strings using pattern matching, optionally, with a map * to check case against (may be NULL). If map is null, match will be case insensitive. @@ -682,32 +464,23 @@ class CoreExport InspIRCd * @param mask The glob or CIDR pattern to match against. * @param map The character map to use when matching. */ - static bool MatchCIDR(const std::string &str, const std::string &mask, unsigned const char *map = NULL); - static bool MatchCIDR(const char *str, const char *mask, unsigned const char *map = NULL); + static bool MatchCIDR(const std::string& str, const std::string& mask, unsigned const char* map = NULL); + static bool MatchCIDR(const char* str, const char* mask, unsigned const char* map = NULL); - /** Call the handler for a given command. - * @param commandname The command whos handler you wish to call - * @param parameters The mode parameters - * @param user The user to execute the command as - * @return True if the command handler was called successfully + /** Matches a hostname and IP against a space delimited list of hostmasks. + * @param masks The space delimited masks to match against. + * @param hostname The hostname to try and match. + * @param ipaddr The IP address to try and match. */ - CmdResult CallCommandHandler(const std::string &commandname, const std::vector<std::string>& parameters, User* user); - - /** Return true if the command is a module-implemented command and the given parameters are valid for it - * @param commandname The command name to check - * @param pcnt The parameter count - * @param user The user to test-execute the command as - * @return True if the command handler is a module command, and there are enough parameters and the user has permission to the command - */ - bool IsValidModuleCommand(const std::string &commandname, int pcnt, User* user); + static bool MatchMask(const std::string& masks, const std::string& hostname, const std::string& ipaddr); /** Return true if the given parameter is a valid nick!user\@host mask * @param mask A nick!user\@host masak to match against * @return True i the mask is valid */ - bool IsValidMask(const std::string &mask); + static bool IsValidMask(const std::string& mask); - /** Strips all color codes from the given string + /** Strips all color and control codes except 001 from the given string * @param sentence The string to strip from */ static void StripColor(std::string &sentence); @@ -718,36 +491,16 @@ class CoreExport InspIRCd static void ProcessColors(file_cache& input); /** Rehash the local server + * @param uuid The uuid of the user who started the rehash, can be empty */ - void RehashServer(); - - /** Check if the given nickmask matches too many users, send errors to the given user - * @param nick A nickmask to match against - * @param user A user to send error text to - * @return True if the nick matches too many users - */ - bool NickMatchesEveryone(const std::string &nick, User* user); - - /** Check if the given IP mask matches too many users, send errors to the given user - * @param ip An ipmask to match against - * @param user A user to send error text to - * @return True if the ip matches too many users - */ - bool IPMatchesEveryone(const std::string &ip, User* user); - - /** Check if the given hostmask matches too many users, send errors to the given user - * @param mask A hostmask to match against - * @param user A user to send error text to - * @return True if the host matches too many users - */ - bool HostMatchesEveryone(const std::string &mask, User* user); + void Rehash(const std::string& uuid = ""); /** Calculate a duration in seconds from a string in the form 1y2w3d4h6m5s * @param str A string containing a time in the form 1y2w3d4h6m5s * (one year, two weeks, three days, four hours, six minutes and five seconds) * @return The total number of seconds */ - long Duration(const std::string &str); + static unsigned long Duration(const std::string& str); /** Attempt to compare a password to a string from the config file. * This will be passed to handling modules which will compare the data @@ -756,26 +509,14 @@ class CoreExport InspIRCd * @param data The data from the config file * @param input The data input by the oper * @param hashtype The hash from the config file - * @return 0 if the strings match, 1 or -1 if they do not - */ - int PassCompare(Extensible* ex, const std::string &data, const std::string &input, const std::string &hashtype); - - /** Check if a given server is a uline. - * An empty string returns true, this is by design. - * @param server The server to check for uline status - * @return True if the server is a uline OR the string is empty + * @return True if the strings match, false if they do not */ - bool ULine(const std::string& server); - - /** Returns true if the uline is 'silent' (doesnt generate - * remote connect notices etc). - */ - bool SilentULine(const std::string& server); + bool PassCompare(Extensible* ex, const std::string& data, const std::string& input, const std::string& hashtype); /** Returns the full version string of this ircd * @return The version string */ - std::string GetVersionString(bool rawversion = false); + std::string GetVersionString(bool getFullVersion = false); /** Attempt to write the process id to a given file * @param filename The PID file to attempt to write to @@ -793,108 +534,64 @@ class CoreExport InspIRCd */ InspIRCd(int argc, char** argv); - /** Send a line of WHOIS data to a user. - * @param user user to send the line to - * @param dest user being WHOISed - * @param numeric Numeric to send - * @param text Text of the numeric - */ - void SendWhoisLine(User* user, User* dest, int numeric, const std::string &text); - - /** Send a line of WHOIS data to a user. - * @param user user to send the line to - * @param dest user being WHOISed - * @param numeric Numeric to send - * @param format Format string for the numeric - * @param ... Parameters for the format string - */ - void SendWhoisLine(User* user, User* dest, int numeric, const char* format, ...) CUSTOM_PRINTF(5, 6); - - /** Handle /WHOIS - */ - void DoWhois(User* user, User* dest,unsigned long signon, unsigned long idle, const char* nick); - - /** Quit a user for excess flood, and if they are not - * fully registered yet, temporarily zline their IP. - * @param current user to quit - */ - caller1<void, User*> FloodQuitUser; - - /** Called to check whether a channel restriction mode applies to a user - * @param User that is attempting some action - * @param Channel that the action is being performed on - * @param Action name - */ - caller3<ModResult, User*, Channel*, const std::string&> OnCheckExemption; - - /** Restart the server. - * This function will not return. If an error occurs, - * it will throw an instance of CoreException. - * @param reason The restart reason to show to all clients - * @throw CoreException An instance of CoreException indicating the error from execv(). - */ - void Restart(const std::string &reason); - /** Prepare the ircd for restart or shutdown. * This function unloads all modules which can be unloaded, * closes all open sockets, and closes the logfile. */ void Cleanup(); - /** This copies the user and channel hash_maps into new hash maps. - * This frees memory used by the hash_map allocator (which it neglects - * to free, most of the time, using tons of ram) - */ - void RehashUsersAndChans(); - - /** Resets the cached max bans value on all channels. - * Called by rehash. + /** Return a time_t as a human-readable string. + * @param format The format to retrieve the date/time in. See `man 3 strftime` + * for more information. If NULL, "%a %b %d %T %Y" is assumed. + * @param curtime The timestamp to convert to a human-readable string. + * @param utc True to convert the time to string as-is, false to convert it to local time first. + * @return A string representing the given date/time. */ - void ResetMaxBans(); + static std::string TimeString(time_t curtime, const char* format = NULL, bool utc = false); - /** Return a time_t as a human-readable string. + /** Compare two strings in a timing-safe way. If the lengths of the strings differ, the function + * returns false immediately (leaking information about the length), otherwise it compares each + * character and only returns after all characters have been compared. + * @param one First string + * @param two Second string + * @return True if the strings match, false if they don't */ - std::string TimeString(time_t curtime); + static bool TimingSafeCompare(const std::string& one, const std::string& two); /** Begin execution of the server. * NOTE: this function NEVER returns. Internally, * it will repeatedly loop. - * @return The return value for this function is undefined. - */ - int Run(); - - /** Adds an extban char to the 005 token. */ - void AddExtBanChar(char c); + void Run(); char* GetReadBuffer() { return this->ReadBuffer; } - friend class TestSuite; + ClientProtocol::RFCEvents& GetRFCEvents() { return rfcevents; } }; ENTRYPOINT; -template<class Cmd> -class CommandModule : public Module +inline void stdalgo::culldeleter::operator()(classbase* item) { - Cmd cmd; - public: - CommandModule() : cmd(this) - { - } + if (item) + ServerInstance->GlobalCulls.AddItem(item); +} - void init() - { - ServerInstance->Modules->AddService(cmd); - } +inline void Channel::Write(ClientProtocol::EventProvider& protoevprov, ClientProtocol::Message& msg, char status, const CUList& except_list) +{ + ClientProtocol::Event event(protoevprov, msg); + Write(event, status, except_list); +} - Version GetVersion() - { - return Version(cmd.name, VF_VENDOR|VF_CORE); - } -}; +inline void LocalUser::Send(ClientProtocol::EventProvider& protoevprov, ClientProtocol::Message& msg) +{ + ClientProtocol::Event event(protoevprov, msg); + Send(event); +} -#endif +#include "numericbuilder.h" +#include "clientprotocolmsg.h" +#include "clientprotocolevent.h" diff --git a/include/inspsocket.h b/include/inspsocket.h index c62c5a250..5bdbfd652 100644 --- a/include/inspsocket.h +++ b/include/inspsocket.h @@ -21,11 +21,12 @@ */ -#ifndef INSPSOCKET_H -#define INSPSOCKET_H +#pragma once #include "timer.h" +class IOHook; + /** * States which a socket may be in */ @@ -87,13 +88,17 @@ class CoreExport SocketTimeout : public Timer * @param fd File descriptor of BufferedSocket * @param thesock BufferedSocket to attach to * @param secs_from_now Seconds from now to time out - * @param now The current time */ - SocketTimeout(int fd, BufferedSocket* thesock, long secs_from_now, time_t now) : Timer(secs_from_now, now), sock(thesock), sfd(fd) { } + SocketTimeout(int fd, BufferedSocket* thesock, unsigned int secs_from_now) + : Timer(secs_from_now) + , sock(thesock) + , sfd(fd) + { + } /** Handle tick event */ - virtual void Tick(time_t now); + bool Tick(time_t now) CXX11_OVERRIDE; }; /** @@ -102,30 +107,184 @@ class CoreExport SocketTimeout : public Timer */ class CoreExport StreamSocket : public EventHandler { - /** Module that handles raw I/O for this socket, or NULL */ - reference<Module> IOHook; - /** Private send queue. Note that individual strings may be shared + public: + /** Socket send queue */ - std::deque<std::string> sendq; - /** Length, in bytes, of the sendq */ - size_t sendq_len; + class SendQueue + { + public: + /** One element of the queue, a continuous buffer + */ + typedef std::string Element; + + /** Sequence container of buffers in the queue + */ + typedef std::deque<Element> Container; + + /** Container iterator + */ + typedef Container::const_iterator const_iterator; + + SendQueue() : nbytes(0) { } + + /** Return whether the queue is empty + * @return True if the queue is empty, false otherwise + */ + bool empty() const { return (nbytes == 0); } + + /** Get the number of individual buffers in the queue + * @return Number of individual buffers in the queue + */ + Container::size_type size() const { return data.size(); } + + /** Get the number of queued bytes + * @return Size in bytes of the data in the queue + */ + size_t bytes() const { return nbytes; } + + /** Get the first buffer of the queue + * @return A reference to the first buffer in the queue + */ + const Element& front() const { return data.front(); } + + /** Get an iterator to the first buffer in the queue. + * The returned iterator cannot be used to make modifications to the queue, + * for that purpose the member functions push_*(), pop_front(), erase_front() and clear() can be used. + * @return Iterator referring to the first buffer in the queue, or end() if there are no elements. + */ + const_iterator begin() const { return data.begin(); } + + /** Get an iterator to the (theoretical) buffer one past the end of the queue. + * @return Iterator referring to one element past the end of the container + */ + const_iterator end() const { return data.end(); } + + /** Remove the first buffer in the queue + */ + void pop_front() + { + nbytes -= data.front().length(); + data.pop_front(); + } + + /** Remove bytes from the beginning of the first buffer + * @param n Number of bytes to remove + */ + void erase_front(Element::size_type n) + { + nbytes -= n; + data.front().erase(0, n); + } + + /** Insert a new buffer at the beginning of the queue + * @param newdata Data to add + */ + void push_front(const Element& newdata) + { + data.push_front(newdata); + nbytes += newdata.length(); + } + + /** Insert a new buffer at the end of the queue + * @param newdata Data to add + */ + void push_back(const Element& newdata) + { + data.push_back(newdata); + nbytes += newdata.length(); + } + + /** Clear the queue + */ + void clear() + { + data.clear(); + nbytes = 0; + } + + void moveall(SendQueue& other) + { + nbytes += other.bytes(); + data.insert(data.end(), other.data.begin(), other.data.end()); + other.clear(); + } + + private: + /** Private send queue. Note that individual strings may be shared. + */ + Container data; + + /** Length, in bytes, of the sendq + */ + size_t nbytes; + }; + + private: + /** The IOHook that handles raw I/O for this socket, or NULL */ + IOHook* iohook; + + /** Send queue of the socket + */ + SendQueue sendq; + /** Error - if nonempty, the socket is dead, and this is the reason. */ std::string error; + + /** Check if the socket has an error set, if yes, call OnError + * @param err Error to pass to OnError() + */ + void CheckError(BufferedSocketError err); + + /** Read data from the socket into the recvq, if successful call OnDataReady() + */ + void DoRead(); + + /** Send as much data contained in a SendQueue object as possible. + * All data which successfully sent will be removed from the SendQueue. + * @param sq SendQueue to flush + */ + void FlushSendQ(SendQueue& sq); + + /** Read incoming data into a receive queue. + * @param rq Receive queue to put incoming data into + * @return < 0 on error or close, 0 if no new data is ready (but the socket is still connected), > 0 if data was read from the socket and put into the recvq + */ + int ReadToRecvQ(std::string& rq); + + /** Read data from a hook chain recursively, starting at 'hook'. + * If 'hook' is NULL, the recvq is filled with data from SocketEngine::Recv(), otherwise it is filled with data from the + * next hook in the chain. + * @param hook Next IOHook in the chain, can be NULL + * @param rq Receive queue to put incoming data into + * @return < 0 on error or close, 0 if no new data is ready (but the socket is still connected), > 0 if data was read from + the socket and put into the recvq + */ + int HookChainRead(IOHook* hook, std::string& rq); + protected: std::string recvq; public: - StreamSocket() : sendq_len(0) {} - inline Module* GetIOHook(); - inline void AddIOHook(Module* m); - inline void DelIOHook(); - /** Handle event from socket engine. - * This will call OnDataReady if there is *new* data in recvq - */ - virtual void HandleEvent(EventType et, int errornum = 0); - /** Dispatched from HandleEvent */ - virtual void DoRead(); - /** Dispatched from HandleEvent */ - virtual void DoWrite(); + StreamSocket() : iohook(NULL) { } + IOHook* GetIOHook() const; + void AddIOHook(IOHook* hook); + void DelIOHook(); + + /** Flush the send queue + */ + void DoWrite(); + + /** Called by the socket engine on a read event + */ + void OnEventHandlerRead() CXX11_OVERRIDE; + + /** Called by the socket engine on a write event + */ + void OnEventHandlerWrite() CXX11_OVERRIDE; + + /** Called by the socket engine on error + * @param errcode Error + */ + void OnEventHandlerError(int errcode) CXX11_OVERRIDE; /** Sets the error message for this socket. Once set, the socket is dead. */ void SetError(const std::string& err) { if (error.empty()) error = err; } @@ -138,6 +297,12 @@ class CoreExport StreamSocket : public EventHandler /** Called when the socket gets an error from socket engine or IO hook */ virtual void OnError(BufferedSocketError e) = 0; + /** Called when the endpoint addresses are changed. + * @param local The new local endpoint. + * @param remote The new remote endpoint. + */ + virtual void OnSetEndPoint(const irc::sockets::sockaddrs& server, const irc::sockets::sockaddrs& remote) { } + /** Send the given data out the socket, either now or when writes unblock */ void WriteData(const std::string& data); @@ -148,14 +313,22 @@ class CoreExport StreamSocket : public EventHandler */ bool GetNextLine(std::string& line, char delim = '\n'); /** Useful for implementing sendq exceeded */ - inline size_t getSendQSize() const { return sendq_len; } + size_t getSendQSize() const; + + SendQueue& GetSendQ() { return sendq; } /** * Close the socket, remove from socket engine, etc */ virtual void Close(); /** This ensures that close is called prior to destructor */ - virtual CullResult cull(); + CullResult cull() CXX11_OVERRIDE; + + /** Get the IOHook of a module attached to this socket + * @param mod Module whose IOHook to return + * @return IOHook belonging to the module or NULL if the module haven't attached an IOHook to this socket + */ + IOHook* GetModHook(Module* mod) const; }; /** * BufferedSocket is an extendable socket class which modules @@ -199,7 +372,7 @@ class CoreExport BufferedSocket : public StreamSocket * @param maxtime Time to wait for connection * @param connectbindip Address to bind to (if NULL, no bind will be done) */ - void DoConnect(const std::string &ipaddr, int aport, unsigned long maxtime, const std::string &connectbindip); + void DoConnect(const std::string& ipaddr, int aport, unsigned int maxtime, const std::string& connectbindip); /** This method is called when an outbound connection on your socket is * completed. @@ -209,7 +382,7 @@ class CoreExport BufferedSocket : public StreamSocket /** When there is data waiting to be read on a socket, the OnDataReady() * method is called. */ - virtual void OnDataReady() = 0; + void OnDataReady() CXX11_OVERRIDE = 0; /** * When an outbound connection fails, and the attempt times out, you @@ -224,14 +397,10 @@ class CoreExport BufferedSocket : public StreamSocket virtual ~BufferedSocket(); protected: - virtual void DoWrite(); - BufferedSocketError BeginConnect(const irc::sockets::sockaddrs& dest, const irc::sockets::sockaddrs& bind, unsigned long timeout); - BufferedSocketError BeginConnect(const std::string &ipaddr, int aport, unsigned long maxtime, const std::string &connectbindip); + void OnEventHandlerWrite() CXX11_OVERRIDE; + BufferedSocketError BeginConnect(const irc::sockets::sockaddrs& dest, const irc::sockets::sockaddrs& bind, unsigned int timeout); + BufferedSocketError BeginConnect(const std::string& ipaddr, int aport, unsigned int maxtime, const std::string& connectbindip); }; -#include "modules.h" - -inline Module* StreamSocket::GetIOHook() { return IOHook; } -inline void StreamSocket::AddIOHook(Module* m) { IOHook = m; } -inline void StreamSocket::DelIOHook() { IOHook = NULL; } -#endif +inline IOHook* StreamSocket::GetIOHook() const { return iohook; } +inline void StreamSocket::DelIOHook() { iohook = NULL; } diff --git a/include/inspstring.h b/include/inspstring.h index a6ef5e552..2501b76ce 100644 --- a/include/inspstring.h +++ b/include/inspstring.h @@ -18,40 +18,38 @@ */ -#ifndef INSPSTRING_H -#define INSPSTRING_H +#pragma once -// This (inspircd_config) is needed as inspstring doesn't pull in the central header -#include "inspircd_config.h" +// This (config) is needed as inspstring doesn't pull in the central header +#include "config.h" #include <cstring> -//#include <cstddef> -#ifndef HAS_STRLCPY -/** strlcpy() implementation for systems that don't have it (linux) */ -CoreExport size_t strlcpy(char *dst, const char *src, size_t siz); -/** strlcat() implementation for systems that don't have it (linux) */ -CoreExport size_t strlcat(char *dst, const char *src, size_t siz); -#endif - -/** charlcat() will append one character to a string using the same - * safety scemantics as strlcat(). - * @param x The string to operate on - * @param y the character to append to the end of x - * @param z The maximum allowed length for z including null terminator - */ -CoreExport int charlcat(char* x,char y,int z); -/** charremove() will remove all instances of a character from a string - * @param mp The string to operate on - * @param remove The character to remove +/** Sets ret to the formated string. last is the last parameter before ..., and format is the format in printf-style */ +#define VAFORMAT(ret, last, format) \ + do { \ + va_list _vaList; \ + va_start(_vaList, last); \ + ret.assign(InspIRCd::Format(_vaList, format)); \ + va_end(_vaList); \ + } while (false); + +/** Compose a hex string from raw data. + * @param raw The raw data to compose hex from (can be NULL if rawsize is 0) + * @param rawsize The size of the raw data buffer + * @return The hex string */ -CoreExport bool charremove(char* mp, char remove); +CoreExport std::string BinToHex(const void* raw, size_t rawsize); -/** Binary to hexadecimal conversion */ -CoreExport std::string BinToHex(const std::string& data); /** Base64 encode */ CoreExport std::string BinToBase64(const std::string& data, const char* table = NULL, char pad = 0); /** Base64 decode */ CoreExport std::string Base64ToBin(const std::string& data, const char* table = NULL); -#endif - +/** Compose a hex string from the data in a std::string. + * @param data The data to compose hex from + * @return The hex string. + */ +inline std::string BinToHex(const std::string& data) +{ + return BinToHex(data.data(), data.size()); +} diff --git a/include/intrusive_list.h b/include/intrusive_list.h new file mode 100644 index 000000000..de013ae38 --- /dev/null +++ b/include/intrusive_list.h @@ -0,0 +1,71 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2013-2014 Attila Molnar <attilamolnar@hush.com> + * + * 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/>. + */ + + +#pragma once + +#include <iterator> + +namespace insp +{ + +struct intrusive_list_def_tag { }; + +template <typename T, typename Tag = intrusive_list_def_tag> class intrusive_list; +template <typename T, typename Tag = intrusive_list_def_tag> class intrusive_list_tail; + +template <typename T, typename Tag = intrusive_list_def_tag> +class intrusive_list_node +{ + T* ptr_next; + T* ptr_prev; + + void unlink() + { + if (ptr_next) + ptr_next->intrusive_list_node<T, Tag>::ptr_prev = this->ptr_prev; + if (ptr_prev) + ptr_prev->intrusive_list_node<T, Tag>::ptr_next = this->ptr_next; + ptr_next = ptr_prev = NULL; + } + + public: + intrusive_list_node() + : ptr_next(NULL) + , ptr_prev(NULL) + { + } + + friend class intrusive_list<T, Tag>; + friend class intrusive_list_tail<T, Tag>; +}; + +} // namespace insp + +// Intrusive list where the list only has a pointer to the head element +#define INSPIRCD_INTRUSIVE_LIST_NAME intrusive_list +#include "intrusive_list_impl.h" +#undef INSPIRCD_INTRUSIVE_LIST_NAME + +// Intrusive list where the list maintains a pointer to both the head and the tail elements. +// Additional methods: back(), push_back(), pop_back() +#define INSPIRCD_INTRUSIVE_LIST_NAME intrusive_list_tail +#define INSPIRCD_INTRUSIVE_LIST_HAS_TAIL +#include "intrusive_list_impl.h" +#undef INSPIRCD_INTRUSIVE_LIST_NAME +#undef INSPIRCD_INTRUSIVE_LIST_HAS_TAIL diff --git a/include/intrusive_list_impl.h b/include/intrusive_list_impl.h new file mode 100644 index 000000000..1dd36b03a --- /dev/null +++ b/include/intrusive_list_impl.h @@ -0,0 +1,172 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2013-2014 Attila Molnar <attilamolnar@hush.com> + * + * 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/>. + */ + + +namespace insp +{ + +template <typename T, typename Tag> +class INSPIRCD_INTRUSIVE_LIST_NAME +{ + public: + class iterator : public std::iterator<std::bidirectional_iterator_tag, T*> + { + T* curr; + + public: + iterator(T* i = NULL) + : curr(i) + { + } + + iterator& operator++() + { + curr = curr->intrusive_list_node<T, Tag>::ptr_next; + return *this; + } + + iterator operator++(int) + { + iterator ret(*this); + operator++(); + return ret; + } + + iterator& operator--() + { + curr = curr->intrusive_list_node<T, Tag>::ptr_prev; + return *this; + } + + iterator operator--(int) + { + iterator ret(*this); + operator--(); + return ret; + } + + bool operator==(const iterator& other) const { return (curr == other.curr); } + bool operator!=(const iterator& other) const { return (curr != other.curr); } + T* operator*() const { return curr; } + }; + + typedef iterator const_iterator; + + INSPIRCD_INTRUSIVE_LIST_NAME() + : listhead(NULL) +#ifdef INSPIRCD_INTRUSIVE_LIST_HAS_TAIL + , listtail(NULL) +#endif + , listsize(0) + { + } + + bool empty() const + { + return (size() == 0); + } + + size_t size() const + { + return listsize; + } + + iterator begin() const + { + return iterator(listhead); + } + + iterator end() const + { + return iterator(); + } + + void pop_front() + { + erase(listhead); + } + + T* front() const + { + return listhead; + } + + void push_front(T* x) + { + if (listsize++) + { + x->intrusive_list_node<T, Tag>::ptr_next = listhead; + listhead->intrusive_list_node<T, Tag>::ptr_prev = x; + } +#ifdef INSPIRCD_INTRUSIVE_LIST_HAS_TAIL + else + listtail = x; +#endif + listhead = x; + } + +#ifdef INSPIRCD_INTRUSIVE_LIST_HAS_TAIL + T* back() const + { + return listtail; + } + + void push_back(T* x) + { + if (listsize++) + { + x->intrusive_list_node<T, Tag>::ptr_prev = listtail; + listtail->intrusive_list_node<T, Tag>::ptr_next = x; + } + else + listhead = x; + listtail = x; + } + + void pop_back() + { + erase(listtail); + } +#endif + + void erase(const iterator& it) + { + erase(*it); + } + + void erase(T* x) + { + if (listhead == x) + listhead = x->intrusive_list_node<T, Tag>::ptr_next; +#ifdef INSPIRCD_INTRUSIVE_LIST_HAS_TAIL + if (listtail == x) + listtail = x->intrusive_list_node<T, Tag>::ptr_prev; +#endif + x->intrusive_list_node<T, Tag>::unlink(); + listsize--; + } + + private: + T* listhead; +#ifdef INSPIRCD_INTRUSIVE_LIST_HAS_TAIL + T* listtail; +#endif + size_t listsize; +}; + +} // namespace insp diff --git a/include/iohook.h b/include/iohook.h new file mode 100644 index 000000000..9ca17d77e --- /dev/null +++ b/include/iohook.h @@ -0,0 +1,167 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2013 Attila Molnar <attilamolnar@hush.com> + * + * 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/>. + */ + + +#pragma once + +class StreamSocket; + +class IOHookProvider : public refcountbase, public ServiceProvider +{ + const bool middlehook; + + public: + enum Type + { + IOH_UNKNOWN, + IOH_SSL + }; + + const Type type; + + /** Constructor + * @param mod Module that owns the IOHookProvider + * @param Name Name of the provider + * @param hooktype One of IOHookProvider::Type + * @param middle True if the IOHook instances created by this hook are subclasses of IOHookMiddle, false otherwise + */ + IOHookProvider(Module* mod, const std::string& Name, Type hooktype = IOH_UNKNOWN, bool middle = false) + : ServiceProvider(mod, Name, SERVICE_IOHOOK), middlehook(middle), type(hooktype) { } + + /** Check if the IOHook provided can appear in the non-last position of a hook chain. + * That is the case if and only if the IOHook instances created are subclasses of IOHookMiddle. + * @return True if the IOHooks provided are subclasses of IOHookMiddle + */ + bool IsMiddle() const { return middlehook; } + + /** Called when the provider should hook an incoming connection and act as being on the server side of the connection. + * This occurs when a bind block has a hook configured and the listener accepts a connection. + * @param sock Socket to hook + * @param client Client IP address and port + * @param server Server IP address and port + */ + virtual void OnAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) = 0; + + /** Called when the provider should hook an outgoing connection and act as being on the client side of the connection. + * @param sock Socket to hook + */ + virtual void OnConnect(StreamSocket* sock) = 0; +}; + +class IOHook : public classbase +{ + public: + /** The IOHookProvider for this hook, contains information about the hook, + * such as the module providing it and the hook type. + */ + reference<IOHookProvider> prov; + + /** Constructor + * @param provider IOHookProvider that creates this object + */ + IOHook(IOHookProvider* provider) + : prov(provider) { } + + /** + * Called when the hooked socket has data to write, or when the socket engine returns it as writable + * @param sock Hooked socket + * @param sendq Send queue to send data from + * @return 1 if the sendq has been completely emptied, 0 if there is + * still data to send, and -1 if there was an error + */ + virtual int OnStreamSocketWrite(StreamSocket* sock, StreamSocket::SendQueue& sendq) = 0; + + /** Called immediately before the hooked socket is closed. When this event is called, shutdown() + * has not yet been called on the socket. + * @param sock Hooked socket + */ + virtual void OnStreamSocketClose(StreamSocket* sock) = 0; + + /** + * Called when the hooked socket has data to read + * @param sock Hooked socket + * @param recvq The receive queue that new data should be appended to + * @return 1 if new data has been read, 0 if no new data is ready (but the + * socket is still connected), -1 if there was an error or close + */ + virtual int OnStreamSocketRead(StreamSocket* sock, std::string& recvq) = 0; +}; + +class IOHookMiddle : public IOHook +{ + /** Data already processed by the IOHook waiting to go down the chain + */ + StreamSocket::SendQueue sendq; + + /** Data waiting to go up the chain + */ + std::string precvq; + + /** Next IOHook in the chain + */ + IOHook* nexthook; + + protected: + /** Get all queued up data which has not yet been passed up the hook chain + * @return RecvQ containing the data + */ + std::string& GetRecvQ() { return precvq; } + + /** Get all queued up data which is ready to go down the hook chain + * @return SendQueue containing all data waiting to go down the hook chain + */ + StreamSocket::SendQueue& GetSendQ() { return sendq; } + + public: + /** Constructor + * @param provider IOHookProvider that creates this object + */ + IOHookMiddle(IOHookProvider* provider) + : IOHook(provider) + , nexthook(NULL) + { + } + + /** Get all queued up data which is ready to go down the hook chain + * @return SendQueue containing all data waiting to go down the hook chain + */ + const StreamSocket::SendQueue& GetSendQ() const { return sendq; } + + /** Get the next IOHook in the chain + * @return Next hook in the chain or NULL if this is the last hook + */ + IOHook* GetNextHook() const { return nexthook; } + + /** Set the next hook in the chain + * @param hook Hook to set as the next hook in the chain + */ + void SetNextHook(IOHook* hook) { nexthook = hook; } + + /** Check if a hook is capable of being the non-last hook in a hook chain and if so, cast it to an IOHookMiddle object. + * @param hook IOHook to check + * @return IOHookMiddle referring to the same hook or NULL + */ + static IOHookMiddle* ToMiddleHook(IOHook* hook) + { + if (hook->prov->IsMiddle()) + return static_cast<IOHookMiddle*>(hook); + return NULL; + } + + friend class StreamSocket; +}; diff --git a/include/isupportmanager.h b/include/isupportmanager.h new file mode 100644 index 000000000..e5eeb599e --- /dev/null +++ b/include/isupportmanager.h @@ -0,0 +1,51 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2013 Peter Powell <petpow@saberuk.com> + * + * 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/>. + */ + + +#pragma once + +/** This class manages the generation and transmission of ISUPPORT. */ +class CoreExport ISupportManager +{ + private: + /** The generated lines which are sent to clients. */ + std::vector<Numeric::Numeric> cachedlines; + + /** Escapes an ISUPPORT token value and appends it to the buffer. + * @param buffer The buffer to append to. + * @param value An ISUPPORT token value. + */ + void AppendValue(std::string& buffer, const std::string& value); + + public: + /** (Re)build the ISUPPORT vector. + * Called by the core on boot after all modules have been loaded, and every time when a module is loaded + * or unloaded. Calls the On005Numeric hook, letting modules manipulate the ISUPPORT tokens. + */ + void Build(); + + /** Returns the cached std::vector of ISUPPORT lines. + * @return A list of Numeric::Numeric objects prepared for sending to users + */ + const std::vector<Numeric::Numeric>& GetLines() const { return cachedlines; } + + /** Send the 005 numerics (ISUPPORT) to a user. + * @param user The user to send the ISUPPORT numerics to + */ + void SendTo(LocalUser* user); +}; diff --git a/include/listmode.h b/include/listmode.h new file mode 100644 index 000000000..5eb77538f --- /dev/null +++ b/include/listmode.h @@ -0,0 +1,222 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.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/>. + */ + + +#pragma once + +/** The base class for list modes, should be inherited. + */ +class CoreExport ListModeBase : public ModeHandler +{ + public: + /** An item in a listmode's list + */ + struct ListItem + { + std::string setter; + std::string mask; + time_t time; + ListItem(const std::string& Mask, const std::string& Setter, time_t Time) + : setter(Setter), mask(Mask), time(Time) { } + }; + + /** Items stored in the channel's list + */ + typedef std::vector<ListItem> ModeList; + + private: + class ChanData + { + public: + ModeList list; + int maxitems; + + ChanData() : maxitems(-1) { } + }; + + /** The number of items a listmode's list may contain + */ + struct ListLimit + { + std::string mask; + unsigned int limit; + ListLimit(const std::string& Mask, unsigned int Limit) : mask(Mask), limit(Limit) { } + bool operator==(const ListLimit& other) const { return (this->mask == other.mask && this->limit == other.limit); } + }; + + /** Max items per channel by name + */ + typedef std::vector<ListLimit> limitlist; + + /** The default maximum list size. */ + static const unsigned int DEFAULT_LIST_SIZE = 100; + + /** Finds the limit of modes that can be placed on the given channel name according to the config + * @param channame The channel name to find the limit for + * @return The maximum number of modes of this type that we allow to be set on the given channel name + */ + unsigned int FindLimit(const std::string& channame); + + /** Returns the limit on the given channel for this mode. + * If the limit is cached then the cached value is returned, + * otherwise the limit is determined using FindLimit() and cached + * for later queries before it is returned + * @param channame The channel name to find the limit for + * @param cd The ChanData associated with channel channame + * @return The maximum number of modes of this type that we allow to be set on the given channel + */ + unsigned int GetLimitInternal(const std::string& channame, ChanData* cd); + + protected: + /** Numeric to use when outputting the list + */ + unsigned int listnumeric; + /** Numeric to indicate end of list + */ + unsigned int endoflistnumeric; + /** String to send for end of list + */ + std::string endofliststring; + /** Automatically tidy up entries + */ + bool tidy; + /** Config tag to check for max items per channel + */ + std::string configtag; + /** Limits on a per-channel basis read from the tag + * specified in ListModeBase::configtag + */ + limitlist chanlimits; + + /** Storage key + */ + SimpleExtItem<ChanData> extItem; + + public: + /** Constructor. + * @param Creator The creator of this class + * @param Name Mode name + * @param modechar Mode character + * @param eolstr End of list string + * @param lnum List numeric + * @param eolnum End of list numeric + * @param autotidy Automatically tidy list entries on add + * @param ctag Configuration tag to get limits from + */ + ListModeBase(Module* Creator, const std::string& Name, char modechar, const std::string &eolstr, unsigned int lnum, unsigned int eolnum, bool autotidy, const std::string &ctag = "banlist"); + + /** Get limit of this mode on a channel + * @param channel The channel to inspect + * @return Maximum number of modes of this type that can be placed on the given channel + */ + unsigned int GetLimit(Channel* channel); + + /** Gets the lower list limit for this listmode. + */ + unsigned int GetLowerLimit(); + + /** Retrieves the list of all modes set on the given channel + * @param channel Channel to get the list from + * @return A list with all modes of this type set on the given channel, can be NULL + */ + ModeList* GetList(Channel* channel); + + /** Display the list for this mode + * See mode.h + * @param user The user to send the list to + * @param channel The channel the user is requesting the list for + */ + void DisplayList(User* user, Channel* channel) CXX11_OVERRIDE; + + /** Tell a user that a list contains no elements. + * Sends 'eolnum' numeric with text 'eolstr', unless overridden (see constructor) + * @param user The user issuing the command + * @param channel The channel that has the empty list + * See mode.h + */ + void DisplayEmptyList(User* user, Channel* channel) CXX11_OVERRIDE; + + /** Remove all instances of the mode from a channel. + * Populates the given modestack with modes that remove every instance of + * this mode from the channel. + * See mode.h for more details. + * @param channel The channel to remove all instances of the mode from + * @param changelist Mode change list to populate with the removal of this mode + */ + void RemoveMode(Channel* channel, Modes::ChangeList& changelist) CXX11_OVERRIDE; + + /** Perform a rehash of this mode's configuration data + */ + void DoRehash(); + + /** Handle the list mode. + * See mode.h + */ + ModeAction OnModeChange(User* source, User*, Channel* channel, std::string ¶meter, bool adding) CXX11_OVERRIDE; + + /** Validate parameters. + * Overridden by implementing module. + * @param user Source user adding the parameter + * @param channel Channel the parameter is being added to + * @param parameter The actual parameter being added + * @return true if the parameter is valid + */ + virtual bool ValidateParam(User* user, Channel* channel, std::string& parameter); + + /** In the event that the mode should be given a parameter, and no parameter was provided, this method is called. + * This allows you to give special information to the user, or handle this any way you like. + * @param user The user issuing the mode change + * @param dest For user mode changes, the target of the mode. For channel mode changes, NULL. + * @param channel For channel mode changes, the target of the mode. For user mode changes, NULL. + * See mode.h + */ + virtual void OnParameterMissing(User* user, User* dest, Channel* channel) CXX11_OVERRIDE; + + /** Tell the user the list is too long. + * Overridden by implementing module. + * @param source Source user adding the parameter + * @param channel Channel the parameter is being added to + * @param parameter The actual parameter being added + */ + virtual void TellListTooLong(User* source, Channel* channel, std::string& parameter); + + /** Tell the user an item is already on the list. + * Overridden by implementing module. + * @param source Source user adding the parameter + * @param channel Channel the parameter is being added to + * @param parameter The actual parameter being added + */ + virtual void TellAlreadyOnList(User* source, Channel* channel, std::string& parameter); + + /** Tell the user that the parameter is not in the list. + * Overridden by implementing module. + * @param source Source user removing the parameter + * @param channel Channel the parameter is being removed from + * @param parameter The actual parameter being removed + */ + virtual void TellNotSet(User* source, Channel* channel, std::string& parameter); +}; + +inline ListModeBase::ModeList* ListModeBase::GetList(Channel* channel) +{ + ChanData* cd = extItem.get(channel); + if (!cd) + return NULL; + + return &cd->list; +} diff --git a/include/logger.h b/include/logger.h index 0fa4bc7cd..5d4a80d9f 100644 --- a/include/logger.h +++ b/include/logger.h @@ -18,8 +18,18 @@ */ -#ifndef LOGGER_H -#define LOGGER_H +#pragma once + +/** Levels at which messages can be logged. */ +enum LogLevel +{ + LOG_RAWIO = 5, + LOG_DEBUG = 10, + LOG_VERBOSE = 20, + LOG_DEFAULT = 30, + LOG_SPARSE = 40, + LOG_NONE = 50 +}; /** Simple wrapper providing periodic flushing to a disk-backed file. */ @@ -31,14 +41,18 @@ class CoreExport FileWriter */ FILE* log; + /** The number of write operations after which we should flush. + */ + unsigned int flush; + /** Number of write operations that have occured */ - int writeops; + unsigned int writeops; public: /** The constructor takes an already opened logfile. */ - FileWriter(FILE* logfile); + FileWriter(FILE* logfile, unsigned int flushcount); /** Write one or more preformatted log lines. * If the data cannot be written immediately, @@ -77,9 +91,11 @@ class CoreExport FileWriter class CoreExport LogStream : public classbase { protected: - int loglvl; + LogLevel loglvl; public: - LogStream(int loglevel) : loglvl(loglevel) + static const char LogHeader[]; + + LogStream(LogLevel loglevel) : loglvl(loglevel) { } @@ -91,18 +107,18 @@ class CoreExport LogStream : public classbase /** Changes the loglevel for this LogStream on-the-fly. * This is needed for -nofork. But other LogStreams could use it to change loglevels. */ - void ChangeLevel(int lvl) { this->loglvl = lvl; } + void ChangeLevel(LogLevel lvl) { this->loglvl = lvl; } /** Called when there is stuff to log for this particular logstream. The derived class may take no action with it, or do what it * wants with the output, basically. loglevel and type are primarily for informational purposes (the level and type of the event triggered) * and msg is, of course, the actual message to log. */ - virtual void OnLog(int loglevel, const std::string &type, const std::string &msg) = 0; + virtual void OnLog(LogLevel loglevel, const std::string &type, const std::string &msg) = 0; }; typedef std::map<FileWriter*, int> FileLogMap; -class CoreExport LogManager +class CoreExport LogManager : public fakederef<LogManager> { private: /** Lock variable, set to true when a log is in progress, which prevents further loggging from happening and creating a loop. @@ -127,7 +143,6 @@ class CoreExport LogManager FileLogMap FileLogs; public: - LogManager(); ~LogManager(); @@ -199,17 +214,15 @@ class CoreExport LogManager /** Logs an event, sending it to all LogStreams registered for the type. * @param type Log message type (ex: "USERINPUT", "MODULE", ...) - * @param loglevel Log message level (DEBUG, VERBOSE, DEFAULT, SPARSE, NONE) + * @param loglevel Log message level (LOG_DEBUG, LOG_VERBOSE, LOG_DEFAULT, LOG_SPARSE, LOG_NONE) * @param msg The message to be logged (literal). */ - void Log(const std::string &type, int loglevel, const std::string &msg); + void Log(const std::string &type, LogLevel loglevel, const std::string &msg); /** Logs an event, sending it to all LogStreams registered for the type. * @param type Log message type (ex: "USERINPUT", "MODULE", ...) - * @param loglevel Log message level (DEBUG, VERBOSE, DEFAULT, SPARSE, NONE) + * @param loglevel Log message level (LOG_DEBUG, LOG_VERBOSE, LOG_DEFAULT, LOG_SPARSE, LOG_NONE) * @param fmt The format of the message to be logged. See your C manual on printf() for details. */ - void Log(const std::string &type, int loglevel, const char *fmt, ...) CUSTOM_PRINTF(4, 5); + void Log(const std::string &type, LogLevel loglevel, const char *fmt, ...) CUSTOM_PRINTF(4, 5); }; - -#endif diff --git a/include/membership.h b/include/membership.h index 436a9371c..6d0bc274f 100644 --- a/include/membership.h +++ b/include/membership.h @@ -1,6 +1,7 @@ /* * InspIRCd -- Internet Relay Chat Daemon * + * Copyright (C) 2012-2014 Attila Molnar <attilamolnar@hush.com> * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org> * * This file is part of InspIRCd. InspIRCd is free software: you can @@ -17,47 +18,102 @@ */ -#ifndef MEMBERSHIP_H -#define MEMBERSHIP_H +#pragma once -class CoreExport Membership : public Extensible +#include "convto.h" + +/** + * Represents a member of a channel. + * A Membership object is created when a user joins a channel, and destroyed when a user leaves + * (via kick, part or quit) a channel. + * All prefix modes a member has is tracked by this object. Moreover, Memberships are Extensibles + * meaning modules can add arbitrary data to them using extensions (see m_delaymsg for an example). + */ +class CoreExport Membership : public Extensible, public insp::intrusive_list_node<Membership> { public: + /** Type of the Membership id + */ + typedef uint64_t Id; + + /** User on the channel + */ User* const user; + + /** Channel the user is on + */ Channel* const chan; - // mode list, sorted by prefix rank, higest first + + /** List of prefix mode letters this member has, + * sorted by prefix rank, highest first + */ std::string modes; - Membership(User* u, Channel* c) : user(u), chan(c) {} - inline bool hasMode(char m) const + + /** Id of this Membership, set by the protocol module, other components should never read or + * write this field. + */ + Id id; + + /** Converts a string to a Membership::Id + * @param str The string to convert + * @return Raw value of type Membership::Id + */ + static Id IdFromString(const std::string& str) { - return modes.find(m) != std::string::npos; + return ConvToNum<Id>(str); } - unsigned int getRank(); -}; -class CoreExport InviteBase -{ - protected: - InviteList invites; + /** Constructor, sets the user and chan fields to the parameters, does NOT update any bookkeeping + * information in the User or the Channel. + * Call Channel::JoinUser() or ForceJoin() to make a user join a channel instead of constructing + * Membership objects directly. + */ + Membership(User* u, Channel* c) : user(u), chan(c) {} - public: - void ClearInvites(); + /** Check if this member has a given prefix mode set + * @param pm Prefix mode to check + * @return True if the member has the prefix mode set, false otherwise + */ + bool HasMode(const PrefixMode* pm) const + { + return (modes.find(pm->GetModeChar()) != std::string::npos); + } - friend class Invitation; -}; + /** Returns the rank of this member. + * The rank of a member is defined as the rank given by the 'strongest' prefix mode a + * member has. See the PrefixMode class description for more info. + * @return The rank of the member + */ + unsigned int getRank(); -class Invitation : public classbase -{ - Invitation(Channel* c, LocalUser* u, time_t timeout) : user(u), chan(c), expiry(timeout) {} + /** Add a prefix character to a user. + * Only the core should call this method, usually from + * within the mode parser or when the first user joins + * the channel (to grant the default privs to them) + * @param mh The mode handler of the prefix mode to associate + * @param adding True if adding the prefix, false when removing + * @return True if a change was made + */ + bool SetPrefix(PrefixMode* mh, bool adding); - public: - LocalUser* const user; - Channel* const chan; - time_t expiry; + /** Get the highest prefix this user has on the channel + * @return A character containing the highest prefix. + * If the user has no prefix, 0 is returned. If the user has multiple prefixes, + * the highest is returned. If you do not recognise the prefix character you + * can get, you can deal with it in a 'proportional' manner compared to known + * prefixes, using GetPrefixValue(). + */ + char GetPrefixChar() const; - ~Invitation(); - static void Create(Channel* c, LocalUser* u, time_t timeout); - static Invitation* Find(Channel* c, LocalUser* u, bool check_expired = true); -}; + /** Return all prefix chars this member has. + * @return A list of all prefix characters. The prefixes will always + * be in rank order, greatest first, as certain IRC clients require + * this when multiple prefixes are used names lists. + */ + std::string GetAllPrefixChars() const; -#endif + /** Sends a server notice to this user in the context of this channel. + * @param text The contents of the message to send. + */ + void WriteNotice(const std::string& text) const; +}; diff --git a/include/message.h b/include/message.h new file mode 100644 index 000000000..7a968d866 --- /dev/null +++ b/include/message.h @@ -0,0 +1,156 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2017 Peter Powell <petpow@saberuk.com> + * + * 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/>. + */ + + +#pragma once + +/** Whether message was a PRIVMSG or a NOTICE. */ +enum MessageType +{ + /** The message is a PRIVMSG. */ + MSG_PRIVMSG, + + /** The message is a NOTICE. */ + MSG_NOTICE +}; + +class CoreExport MessageDetails +{ + public: + /** Whether to echo the message at all. */ + bool echo; + + /* Whether to send the original message back to clients with echo-message support. */ + bool echo_original; + + /** The users who are exempted from receiving this message. */ + CUList exemptions; + + /* The original message as sent by the user. */ + const std::string original_text; + + /** IRCv3 message tags sent to the server by the user. */ + const ClientProtocol::TagMap tags_in; + + /** IRCv3 message tags sent out to users who get this message. */ + ClientProtocol::TagMap tags_out; + + /** The message which will be sent to clients. */ + std::string text; + + /** The type of message. */ + const MessageType type; + + /** Determines whether the specified message is a CTCP. If the specified message + * is a CTCP then the CTCP name and CTCP body are extracted and stored in the + * name and body references. + * @param name The location to store the parsed CTCP name. + * @param body The location to store the parsed CTCP body. + */ + virtual bool IsCTCP(std::string& name, std::string& body) const = 0; + + /** Determines whether the specified message is a CTCP. If the specified message + * is a CTCP then the CTCP name is extracted and stored in the name reference. + * @param name The location to store the parsed CTCP name. + */ + virtual bool IsCTCP(std::string& name) const = 0; + + /** Determines whether the specified message is a CTCP. */ + virtual bool IsCTCP() const = 0; + + protected: + MessageDetails(MessageType mt, const std::string& msg, const ClientProtocol::TagMap& tags) + : echo(true) + , echo_original(false) + , original_text(msg) + , tags_in(tags) + , text(msg) + , type(mt) + { + } +}; + +/** Represents the target of a message (NOTICE, PRIVMSG, etc). */ +class CoreExport MessageTarget +{ + public: + /** An enumeration of possible message target types. */ + enum TargetType + { + /** The target of the message is a user. */ + TYPE_USER, + + /** The target of the message is a channel. */ + TYPE_CHANNEL, + + /** The target of the message is a server. */ + TYPE_SERVER + }; + + private: + /** The target of the message. */ + void* dest; + + public: + /** If type is TYPE_CHANNEL and the user specified a status rank. */ + char status; + + /** The type of the target of the message. If this is TYPE_CHANNEL then dest + * is a Channel*, TYPE_USER then dest is a User*, and TYPE_SERVER then dest is + * a std::string* containing a server glob. + */ + MessageTarget::TargetType type; + + /** Initialises a new channel message target. + * @param channel The channel which is the target of the message. + * @param statuschar The lowest status rank that the message is being sent to. + */ + MessageTarget(Channel* channel, char statuschar) + : dest(channel) + , status(statuschar) + , type(TYPE_CHANNEL) + { + } + + /** Initialises a new user message target. + * @param user The user which is the target of the message. + */ + MessageTarget(User* user) + : dest(user) + , status(0) + , type(TYPE_USER) + { + } + + /** Initialises a new server message target. + * @param server The server glob which is the target of the message. + */ + MessageTarget(std::string* server) + : dest(server) + , status(0) + , type(TYPE_SERVER) + { + } + + /** Retrieves the target of this message. */ + template<typename T> + T* Get() const + { + return static_cast<T*>(dest); + } +}; diff --git a/include/mode.h b/include/mode.h index 1dab442d4..fe02838b2 100644 --- a/include/mode.h +++ b/include/mode.h @@ -20,10 +20,10 @@ */ -#ifndef MODE_H -#define MODE_H +#pragma once #include "ctables.h" +#include "modechange.h" /** * Holds the values for different type of modes @@ -47,17 +47,6 @@ enum ModeAction }; /** - * Used to mask off the mode types in the mode handler - * array. Used in a simple two instruction hashing function - * "(modeletter - 65) OR mask" - */ -enum ModeMasks -{ - MASK_USER = 128, /* A user mode */ - MASK_CHANNEL = 0 /* A channel mode */ -}; - -/** * These fixed values can be used to proportionally compare module-defined prefixes to known values. * For example, if your module queries a Channel, and is told that user 'joebloggs' has the prefix * '$', and you dont know what $ means, then you can compare it to these three values to determine @@ -85,6 +74,10 @@ enum ParamSpec PARAM_ALWAYS }; +class PrefixMode; +class ListModeBase; +class ParamModeBase; + /** Each mode is implemented by ONE ModeHandler class. * You must derive ModeHandler and add the child class to * the list of modes handled by the ircd, using @@ -101,12 +94,23 @@ enum ParamSpec */ class CoreExport ModeHandler : public ServiceProvider { - protected: - /** - * The mode parameter translation type + public: + typedef size_t Id; + + enum Class + { + MC_PREFIX, + MC_LIST, + MC_PARAM, + MC_OTHER + }; + + private: + /** The opaque id of this mode assigned by the mode parser */ - TranslateType m_paramtype; + Id modeid; + protected: /** What kind of parameters does the mode take? */ ParamSpec parameters_taken; @@ -116,10 +120,6 @@ class CoreExport ModeHandler : public ServiceProvider */ char mode; - /** Mode prefix, or 0 - */ - char prefix; - /** * True if the mode requires oper status * to set. @@ -144,10 +144,15 @@ class CoreExport ModeHandler : public ServiceProvider */ ModeType m_type; - /** The prefix char needed on channel to use this mode, - * only checked for channel modes + /** The object type of this mode handler */ - int levelrequired; + const Class type_id; + + /** The prefix rank required to set this mode on channels. */ + unsigned int ranktoset; + + /** The prefix rank required to unset this mode on channels. */ + unsigned int ranktounset; public: /** @@ -159,56 +164,85 @@ class CoreExport ModeHandler : public ServiceProvider * @param modeletter The mode letter you wish to handle * @param params Parameters taken by the mode * @param type Type of the mode (MODETYPE_USER or MODETYPE_CHANNEL) + * @param mclass The object type of this mode handler, one of ModeHandler::Class */ - ModeHandler(Module* me, const std::string& name, char modeletter, ParamSpec params, ModeType type); - virtual CullResult cull(); + ModeHandler(Module* me, const std::string& name, char modeletter, ParamSpec params, ModeType type, Class mclass = MC_OTHER); + CullResult cull() CXX11_OVERRIDE; virtual ~ModeHandler(); + + /** Register this object in the ModeParser + */ + void RegisterService() CXX11_OVERRIDE; + /** * Returns true if the mode is a list mode */ - bool IsListMode(); + bool IsListMode() const { return list; } + /** - * Mode prefix or 0. If this is defined, you should - * also implement GetPrefixRank() to return an integer - * value for this mode prefix. + * Check whether this mode is a prefix mode + * @return non-NULL if this mode is a prefix mode, NULL otherwise */ - inline char GetPrefix() const { return prefix; } + PrefixMode* IsPrefixMode(); + /** - * Get the 'value' of this modes prefix. - * determines which to display when there are multiple. - * The mode with the highest value is ranked first. See the - * PrefixModeValue enum and Channel::GetPrefixValue() for - * more information. + * Check whether this mode is a prefix mode + * @return non-NULL if this mode is a prefix mode, NULL otherwise */ - virtual unsigned int GetPrefixRank(); + const PrefixMode* IsPrefixMode() const; + /** - * Returns the mode's type + * Check whether this mode handler inherits from ListModeBase + * @return non-NULL if this mode handler inherits from ListModeBase, NULL otherwise */ - inline ModeType GetModeType() const { return m_type; } + ListModeBase* IsListModeBase(); + + /** + * Check whether this mode handler inherits from ListModeBase + * @return non-NULL if this mode handler inherits from ListModeBase, NULL otherwise + */ + const ListModeBase* IsListModeBase() const; + + /** + * Check whether this mode handler inherits from ParamModeBase + * @return non-NULL if this mode handler inherits from ParamModeBase, NULL otherwise + */ + ParamModeBase* IsParameterMode(); + + /** + * Check whether this mode handler inherits from ParamModeBase + * @return non-NULL if this mode handler inherits from ParamModeBase, NULL otherwise + */ + const ParamModeBase* IsParameterMode() const; + /** - * Returns the mode's parameter translation type + * Returns the mode's type */ - inline TranslateType GetTranslateType() const { return m_paramtype; } + inline ModeType GetModeType() const { return m_type; } /** * Returns true if the mode can only be set/unset by an oper */ inline bool NeedsOper() const { return oper; } /** - * Returns the number of parameters for the mode. Any non-zero - * value should be considered to be equivalent to one. - * @param adding If this is true, the number of parameters required to set the mode should be returned, otherwise the number of parameters required to unset the mode shall be returned. - * @return The number of parameters the mode expects + * Check if the mode needs a parameter for adding or removing + * @param adding True to check if the mode needs a parameter when setting, false to check if the mode needs a parameter when unsetting + * @return True if the mode needs a parameter for the specified action, false if it doesn't */ - int GetNumParams(bool adding); + bool NeedsParam(bool adding) const; /** * Returns the mode character this handler handles. * @return The mode character */ - inline char GetModeChar() { return mode; } + char GetModeChar() const { return mode; } + + /** Return the id of this mode which is used in User::modes and + * Channel::modes as the index to determine whether a mode is set. + */ + Id GetId() const { return modeid; } /** For user modes, return the current parameter, if any */ - virtual std::string GetUserParameter(User* useor); + virtual std::string GetUserParameter(const User* user) const; /** * Called when a channel mode change access check for your mode occurs. @@ -269,28 +303,138 @@ class CoreExport ModeHandler : public ServiceProvider virtual bool ResolveModeConflict(std::string &their_param, const std::string &our_param, Channel* channel); /** - * When a MODETYPE_USER mode handler is being removed, the server will call this method for every user on the server. - * Your mode handler should remove its user mode from the user by sending the appropriate server modes using - * InspIRCd::SendMode(). The default implementation of this method can remove simple modes which have no parameters, - * and can be used when your mode is of this type, otherwise you must implement a more advanced version of it to remove - * your mode properly from each user. + * When a MODETYPE_USER mode handler is being removed, the core will call this method for every user on the server. + * The usermode will be removed using the appropiate server mode using InspIRCd::SendMode(). * @param user The user which the server wants to remove your mode from - * @param stack The mode stack to add the mode change to */ - virtual void RemoveMode(User* user, irc::modestacker* stack = NULL); + void RemoveMode(User* user); /** * When a MODETYPE_CHANNEL mode handler is being removed, the server will call this method for every channel on the server. - * Your mode handler should remove its user mode from the channel by sending the appropriate server modes using - * InspIRCd::SendMode(). The default implementation of this method can remove simple modes which have no parameters, - * and can be used when your mode is of this type, otherwise you must implement a more advanced version of it to remove - * your mode properly from each channel. Note that in the case of listmodes, you should remove the entire list of items. + * The mode handler has to populate the given modestacker with mode changes that remove the mode from the channel. + * The default implementation of this method can remove all kinds of channel modes except listmodes. + * In the case of listmodes, the entire list of items must be added to the modestacker (which is handled by ListModeBase, + * so if you inherit from it or your mode can be removed by the default implementation then you do not have to implement + * this function). * @param channel The channel which the server wants to remove your mode from - * @param stack The mode stack to add the mode change to + * @param changelist Mode change list to populate with the removal of this mode */ - virtual void RemoveMode(Channel* channel, irc::modestacker* stack = NULL); + virtual void RemoveMode(Channel* channel, Modes::ChangeList& changelist); - inline unsigned int GetLevelRequired() const { return levelrequired; } + /** Retrieves the level required to modify this mode. + * @param adding Whether the mode is being added or removed. + */ + inline unsigned int GetLevelRequired(bool adding) const + { + return adding ? ranktoset : ranktounset; + } + + friend class ModeParser; +}; + +/** + * Prefix modes are channel modes that grant a specific rank to members having prefix mode set. + * They require a parameter when setting and unsetting; the parameter is always a member of the channel. + * A prefix mode may be set on any number of members on a channel, but for a given member a given prefix + * mode is either set or not set, in other words members cannot have the same prefix mode set more than once. + * + * A rank of a member is defined as the rank given by the 'strongest' prefix mode that member has. + * Other parts of the IRCd use this rank to determine whether a channel action is allowable for a user or not. + * The rank of a prefix mode is constant, i.e. the same rank value is given to all users having that prefix mode set. + * + * Note that it is possible that the same action requires a different rank on a different channel; + * for example changing the topic on a channel having +t set requires a rank that is >= than the rank of a halfop, + * but there is no such restriction when +t isn't set. + */ +class CoreExport PrefixMode : public ModeHandler +{ + protected: + /** The prefix character granted by this mode. '@' for op, '+' for voice, etc. + * If 0, this mode does not have a visible prefix character. + */ + char prefix; + + /** The prefix rank of this mode, used to compare prefix + * modes + */ + unsigned int prefixrank; + + /** Whether a client with this prefix can remove it from themself. */ + bool selfremove; + + public: + /** + * Constructor + * @param Creator The module creating this mode + * @param Name The user-friendly one word name of the prefix mode, e.g.: "op", "voice" + * @param ModeLetter The mode letter of this mode + * @param Rank Rank given by this prefix mode, see explanation above + * @param PrefixChar Prefix character, or 0 if the mode has no prefix character + */ + PrefixMode(Module* Creator, const std::string& Name, char ModeLetter, unsigned int Rank = 0, char PrefixChar = 0); + + /** + * Called when a channel mode change access check for your mode occurs. + * @param source Contains the user setting the mode. + * @param channel contains the destination channel the modes are being set on. + * @param parameter The parameter for your mode. This is modifiable. + * @param adding This value is true when the mode is being set, or false when it is being unset. + * @return allow, deny, or passthru to check against the required level + */ + ModResult AccessCheck(User* source, Channel* channel, std::string ¶meter, bool adding) CXX11_OVERRIDE; + + /** + * Handles setting and unsetting the prefix mode. + * Finds the given member of the given channel, if it's not found an error message is sent to 'source' + * and MODEACTION_DENY is returned. Otherwise the mode change is attempted. + * @param source Source of the mode change, an error message is sent to this user if the target is not found + * @param dest Unused + * @param channel The channel the mode change is happening on + * @param param The nickname or uuid of the target user + * @param adding True when the mode is being set, false when it is being unset + * @return MODEACTION_ALLOW if the change happened, MODEACTION_DENY if no change happened + * The latter occurs either when the member cannot be found or when the member already has this prefix set + * (when setting) or doesn't have this prefix set (when unsetting). + */ + ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& param, bool adding) CXX11_OVERRIDE; + + /** + * Updates the configuration of this prefix. + * @param rank The prefix rank of this mode. + * @param setrank The prefix rank required to set this mode on channels. + * @param unsetrank The prefix rank required to set this unmode on channels. + * @param selfrm Whether a client with this prefix can remove it from themself. + */ + void Update(unsigned int rank, unsigned int setrank, unsigned int unsetrank, bool selfrm); + + /** + * Removes this prefix mode from all users on the given channel + * @param channel The channel which the server wants to remove your mode from + * @param changelist Mode change list to populate with the removal of this mode + */ + void RemoveMode(Channel* channel, Modes::ChangeList& changelist) CXX11_OVERRIDE; + + + /** + * Determines whether a user with this prefix mode can remove it. + */ + bool CanSelfRemove() const { return selfremove; } + + /** + * Mode prefix or 0. If this is defined, you should + * also implement GetPrefixRank() to return an integer + * value for this mode prefix. + */ + char GetPrefix() const { return prefix; } + + /** + * Get the 'value' of this modes prefix. + * determines which to display when there are multiple. + * The mode with the highest value is ranked first. See the + * PrefixModeValue enum and Channel::GetPrefixValue() for + * more information. + */ + unsigned int GetPrefixRank() const { return prefixrank; } }; /** A prebuilt mode handler which handles a simple user mode, e.g. no parameters, usable by any user, with no extra @@ -303,8 +447,7 @@ class CoreExport SimpleUserModeHandler : public ModeHandler public: SimpleUserModeHandler(Module* Creator, const std::string& Name, char modeletter) : ModeHandler(Creator, Name, modeletter, PARAM_NONE, MODETYPE_USER) {} - virtual ~SimpleUserModeHandler() {} - virtual ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding); + ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& parameter, bool adding) CXX11_OVERRIDE; }; /** A prebuilt mode handler which handles a simple channel mode, e.g. no parameters, usable by any user, with no extra @@ -317,18 +460,7 @@ class CoreExport SimpleChannelModeHandler : public ModeHandler public: SimpleChannelModeHandler(Module* Creator, const std::string& Name, char modeletter) : ModeHandler(Creator, Name, modeletter, PARAM_NONE, MODETYPE_CHANNEL) {} - virtual ~SimpleChannelModeHandler() {} - virtual ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding); -}; - -class CoreExport ParamChannelModeHandler : public ModeHandler -{ - public: - ParamChannelModeHandler(Module* Creator, const std::string& Name, char modeletter) - : ModeHandler(Creator, Name, modeletter, PARAM_SETONLY, MODETYPE_CHANNEL) {} - virtual ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding); - /** Validate the parameter - you may change the value to normalize it. Return true if it is valid. */ - virtual bool ParamValidate(std::string& parameter); + ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& parameter, bool adding) CXX11_OVERRIDE; }; /** @@ -339,11 +471,12 @@ class CoreExport ParamChannelModeHandler : public ModeHandler */ class CoreExport ModeWatcher : public classbase { - protected: + private: /** - * The mode letter this class is watching + * The mode name this class is watching */ - char mode; + const std::string mode; + /** * The mode type being watched (user or channel) */ @@ -354,22 +487,23 @@ class CoreExport ModeWatcher : public classbase /** * The constructor initializes the mode and the mode type */ - ModeWatcher(Module* creator, char modeletter, ModeType type); + ModeWatcher(Module* creator, const std::string& modename, ModeType type); /** * The default destructor does nothing. */ virtual ~ModeWatcher(); /** - * Get the mode character being watched - * @return The mode character being watched + * Get the mode name being watched + * @return The mode name being watched */ - char GetModeChar(); + const std::string& GetModeName() const { return mode; } + /** * Get the mode type being watched * @return The mode type being watched (user or channel) */ - ModeType GetModeType(); + ModeType GetModeType() const { return m_type; } /** * Before the mode character is processed by its handler, this method will be called. @@ -380,11 +514,10 @@ class CoreExport ModeWatcher : public classbase * If you alter the parameter you are given, the mode handler will see your atered version * when it handles the mode. * @param adding True if the mode is being added and false if it is being removed - * @param type The mode type, either MODETYPE_USER or MODETYPE_CHANNEL * @return True to allow the mode change to go ahead, false to abort it. If you abort the * change, the mode handler (and ModeWatcher::AfterMode()) will never see the mode change. */ - virtual bool BeforeMode(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding, ModeType type); + virtual bool BeforeMode(User* source, User* dest, Channel* channel, std::string& parameter, bool adding); /** * After the mode character has been processed by the ModeHandler, this method will be called. * @param source The sender of the mode @@ -393,68 +526,120 @@ class CoreExport ModeWatcher : public classbase * @param parameter The parameter of the mode, if the mode is supposed to have a parameter. * You cannot alter the parameter here, as the mode handler has already processed it. * @param adding True if the mode is being added and false if it is being removed - * @param type The mode type, either MODETYPE_USER or MODETYPE_CHANNEL */ - virtual void AfterMode(User* source, User* dest, Channel* channel, const std::string ¶meter, bool adding, ModeType type); + virtual void AfterMode(User* source, User* dest, Channel* channel, const std::string& parameter, bool adding); }; -typedef std::vector<ModeWatcher*>::iterator ModeWatchIter; - /** The mode parser handles routing of modes and handling of mode strings. * It marshalls, controls and maintains both ModeWatcher and ModeHandler classes, * parses client to server MODE strings for user and channel modes, and performs * processing for the 004 mode list numeric, amongst other things. */ -class CoreExport ModeParser +class CoreExport ModeParser : public fakederef<ModeParser> { + public: + static const ModeHandler::Id MODEID_MAX = 64; + + /** Type of the container that maps mode names to ModeHandlers + */ + typedef TR1NS::unordered_map<std::string, ModeHandler*, irc::insensitive, irc::StrHashComp> ModeHandlerMap; + private: + /** Type of the container that maps mode names to ModeWatchers + */ + typedef insp::flat_multimap<std::string, ModeWatcher*> ModeWatcherMap; + + /** Last item in the ModeType enum + */ + static const unsigned int MODETYPE_LAST = 2; + /** Mode handlers for each mode, to access a handler subtract * 65 from the ascii value of the mode letter. * The upper bit of the value indicates if its a usermode * or a channel mode, so we have 256 of them not 64. */ - ModeHandler* modehandlers[256]; - /** Mode watcher classes arranged in the same way as the - * mode handlers, except for instead of having 256 of them - * we have 256 lists of them. + ModeHandler* modehandlers[MODETYPE_LAST][128]; + + /** An array of mode handlers indexed by the mode id */ - std::vector<ModeWatcher*> modewatchers[256]; - /** Displays the current modes of a channel or user. - * Used by ModeParser::Process. + ModeHandler* modehandlersbyid[MODETYPE_LAST][MODEID_MAX]; + + /** A map of mode handlers keyed by their name */ - void DisplayCurrentModes(User *user, User* targetuser, Channel* targetchannel, const char* text); - /** Displays the value of a list mode - * Used by ModeParser::Process. + ModeHandlerMap modehandlersbyname[MODETYPE_LAST]; + + /** Lists of mode handlers by type */ - void DisplayListModes(User* user, Channel* chan, std::string &mode_sequence); + struct + { + /** List of mode handlers that inherit from ListModeBase + */ + std::vector<ListModeBase*> list; - /** - * Attempts to apply a mode change to a user or channel + /** List of mode handlers that inherit from PrefixMode + */ + std::vector<PrefixMode*> prefix; + } mhlist; + + /** Mode watcher classes */ - ModeAction TryMode(User* user, User* targu, Channel* targc, bool adding, unsigned char mode, std::string ¶m, bool SkipACL); + ModeWatcherMap modewatchermap; - /** The string representing the last set of modes to be parsed. - * Use GetLastParse() to get this value, to be used for display purposes. + /** Last processed mode change */ - std::string LastParse; - std::vector<std::string> LastParseParams; - std::vector<TranslateType> LastParseTranslate; + Modes::ChangeList LastChangeList; - unsigned int sent[256]; + /** + * Attempts to apply a mode change to a user or channel + */ + ModeAction TryMode(User* user, User* targu, Channel* targc, Modes::Change& mcitem, bool SkipACL); - unsigned int seq; + /** Allocates an unused id for the given mode type, throws a ModuleException if out of ids. + * @param mt The type of the mode to allocate the id for + * @return The id + */ + ModeHandler::Id AllocateModeId(ModeType mt); public: + typedef std::vector<ListModeBase*> ListModeList; + typedef std::vector<PrefixMode*> PrefixModeList; + + typedef unsigned int ModeProcessFlag; + enum ModeProcessFlags + { + /** If only this flag is specified, the mode change will be global + * and parameter modes will have their parameters explicitly set + * (not merged). This is the default. + */ + MODE_NONE = 0, + + /** If this flag is set then the parameters of non-listmodes will be + * merged according to their conflict resolution rules. + * Does not affect user modes, channel modes without a parameter and + * listmodes. + */ + MODE_MERGE = 1, + + /** If this flag is set then the linking module will ignore the mode change + * and not send it to other servers. The mode change will be processed + * locally and sent to local user(s) as usual. + */ + MODE_LOCALONLY = 2, + + /** If this flag is set then the mode change will be subject to access checks. + * For more information see the documentation of the PrefixMode class, + * ModeHandler::ranktoset and ModeHandler::AccessCheck(). + * Modules may explicitly allow a mode change regardless of this flag by returning + * MOD_RES_ALLOW from the OnPreMode hook. Only affects channel mode changes. + */ + MODE_CHECKACCESS = 4 + }; - /** The constructor initializes all the RFC basic modes by using ModeParserAddMode(). - */ ModeParser(); ~ModeParser(); - /** Used to check if user 'd' should be allowed to do operation 'MASK' on channel 'chan'. - * for example, should 'user A' be able to 'op' on 'channel B'. - */ - User* SanityChecks(User *user,const char *dest,Channel *chan,int status); + static bool IsModeChar(char chr); + /** Tidy a banmask. This makes a banmask 'acceptable' if fields are left out. * E.g. * @@ -469,18 +654,15 @@ class CoreExport ModeParser * This method can be used on both IPV4 and IPV6 user masks. */ static void CleanMask(std::string &mask); - /** Get the last string to be processed, as it was sent to the user or channel. - * Use this to display a string you just sent to be parsed, as the actual output - * may be different to what you sent after it has been 'cleaned up' by the parser. - * @return Last parsed string, as seen by users. - */ - const std::string& GetLastParse(); - const std::vector<std::string>& GetLastParseParams() { return LastParseParams; } - const std::vector<TranslateType>& GetLastParseTranslate() { return LastParseTranslate; } + + /** Gets the last mode change to be processed. */ + const Modes::ChangeList& GetLastChangeList() const { return LastChangeList; } + /** Add a mode to the mode parser. - * @return True if the mode was successfully added. + * Throws a ModuleException if the mode cannot be added. */ - bool AddMode(ModeHandler* mh); + void AddMode(ModeHandler* mh); + /** Delete a mode from the mode parser. * When a mode is deleted, the mode handler will be called * for every user (if it is a user mode) or for every channel @@ -496,9 +678,9 @@ class CoreExport ModeParser * triggered. See the documentation of class ModeWatcher for more * information. * @param mw The ModeWatcher you want to add - * @return True if the ModeWatcher was added correctly */ - bool AddModeWatcher(ModeWatcher* mw); + void AddModeWatcher(ModeWatcher* mw); + /** Delete a mode watcher. * A mode watcher is triggered before and after a mode handler is * triggered. See the documentation of class ModeWatcher for more @@ -507,15 +689,56 @@ class CoreExport ModeParser * @return True if the ModeWatcher was deleted correctly */ bool DelModeWatcher(ModeWatcher* mw); - /** Process a set of mode changes from a server or user. - * @param parameters The parameters of the mode change, in the format - * they would be from a MODE command. - * @param user The user setting or removing the modes. When the modes are set - * by a server, an 'uninitialized' User is used, where *user\::nick == NULL - * and *user->server == NULL. - * @param merge Should the mode parameters be merged? - */ - void Process(const std::vector<std::string>& parameters, User *user, bool merge = false); + + /** Process a list of mode changes entirely. If the mode changes do not fit into one MODE line + * then multiple MODE lines are generated. + * @param user The source of the mode change, can be a server user. + * @param targetchannel Channel to apply the mode change on. NULL if changing modes on a channel. + * @param targetuser User to apply the mode change on. NULL if changing modes on a user. + * @param changelist Modes to change in form of a Modes::ChangeList. + * @param flags Optional flags controlling how the mode change is processed, + * defaults to MODE_NONE. + */ + void Process(User* user, Channel* targetchannel, User* targetuser, Modes::ChangeList& changelist, ModeProcessFlag flags = MODE_NONE); + + /** Process a single MODE line's worth of mode changes, taking max modes and line length limits + * into consideration. Return value indicates how many modes were processed. + * @param user The source of the mode change, can be a server user. + * @param targetchannel Channel to apply the mode change on. NULL if changing modes on a channel. + * @param targetuser User to apply the mode change on. NULL if changing modes on a user. + * @param changelist Modes to change in form of a Modes::ChangeList. May not process + * the entire list due to MODE line length and max modes limitations. + * @param flags Optional flags controlling how the mode change is processed, + * defaults to MODE_NONE. + * @param beginindex Index of the first element in changelist to process. Mode changes before + * the element with this index are ignored. + * @return Number of mode changes processed from changelist. + */ + unsigned int ProcessSingle(User* user, Channel* targetchannel, User* targetuser, Modes::ChangeList& changelist, ModeProcessFlag flags = MODE_NONE, unsigned int beginindex = 0); + + /** Turn a list of parameters compatible with the format of the MODE command into + * Modes::ChangeList form. All modes are processed, regardless of max modes. Unknown modes + * are skipped. + * @param user The source of the mode change, can be a server user. Error numerics are sent to + * this user. + * @param type MODETYPE_USER if this is a user mode change or MODETYPE_CHANNEL if this + * is a channel mode change. + * @param parameters List of strings describing the mode change to convert to a ChangeList. + * Must be using the same format as the parameters of a MODE command. + * @param changelist ChangeList object to populate. + * @param beginindex Index of the first element that is part of the MODE list in the parameters + * container. Defaults to 1. + * @param endindex Index of the first element that is not part of the MODE list. By default, + * the entire container is considered part of the MODE list. + */ + void ModeParamsToChangeList(User* user, ModeType type, const std::vector<std::string>& parameters, Modes::ChangeList& changelist, unsigned int beginindex = 1, unsigned int endindex = UINT_MAX); + + /** Find the mode handler for a given mode name and type. + * @param modename The mode name to search for. + * @param mt Type of mode to search for, user or channel. + * @return A pointer to a ModeHandler class, or NULL of there isn't a handler for the given mode name. + */ + ModeHandler* FindMode(const std::string& modename, ModeType mt); /** Find the mode handler for a given mode and type. * @param modeletter mode letter to search for @@ -524,27 +747,18 @@ class CoreExport ModeParser */ ModeHandler* FindMode(unsigned const char modeletter, ModeType mt); + /** Find the mode handler for the given prefix mode + * @param modeletter The mode letter to search for + * @return A pointer to the PrefixMode or NULL if the mode wasn't found or it isn't a prefix mode + */ + PrefixMode* FindPrefixMode(unsigned char modeletter); + /** Find a mode handler by its prefix. * If there is no mode handler with the given prefix, NULL will be returned. * @param pfxletter The prefix to find, e.g. '@' * @return The mode handler which handles this prefix, or NULL if there is none. */ - ModeHandler* FindPrefix(unsigned const char pfxletter); - - /** Returns a list of mode characters which are usermodes. - * This is used in the 004 numeric when users connect. - */ - std::string UserModeList(); - - /** Returns a list of channel mode characters which are listmodes. - * This is used in the 004 numeric when users connect. - */ - std::string ChannelModeList(); - - /** Returns a list of channel mode characters which take parameters. - * This is used in the 004 numeric when users connect. - */ - std::string ParaModeList(); + PrefixMode* FindPrefix(unsigned const char pfxletter); /** Generates a list of modes, comma seperated by type: * 1; Listmodes EXCEPT those with a prefix @@ -552,14 +766,63 @@ class CoreExport ModeParser * 3; Modes that only take a param when adding * 4; Modes that dont take a param */ - std::string GiveModeList(ModeMasks m); - - static bool PrefixComparison(ModeHandler* one, ModeHandler* two); + std::string GiveModeList(ModeType mt); /** This returns the PREFIX=(ohv)@%+ section of the 005 numeric, or * just the "@%+" part if the parameter false */ std::string BuildPrefixes(bool lettersAndModes = true); + + /** Get a list of all mode handlers that inherit from ListModeBase + * @return A list containing ListModeBase modes + */ + const ListModeList& GetListModes() const { return mhlist.list; } + + /** Get a list of all prefix modes + * @return A list containing all prefix modes + */ + const PrefixModeList& GetPrefixModes() const { return mhlist.prefix; } + + /** Get a mode name -> ModeHandler* map containing all modes of the given type + * @param mt Type of modes to return, MODETYPE_USER or MODETYPE_CHANNEL + * @return A map of mode handlers of the given type + */ + const ModeHandlerMap& GetModes(ModeType mt) const { return modehandlersbyname[mt]; } + + /** Show the list of a list mode to a user. Modules can deny the listing. + * @param user User to show the list to. + * @param chan Channel to show the list of. + * @param mh List mode to show the list of. + */ + void ShowListModeList(User* user, Channel* chan, ModeHandler* mh); }; -#endif +inline PrefixMode* ModeHandler::IsPrefixMode() +{ + return (this->type_id == MC_PREFIX ? static_cast<PrefixMode*>(this) : NULL); +} + +inline const PrefixMode* ModeHandler::IsPrefixMode() const +{ + return (this->type_id == MC_PREFIX ? static_cast<const PrefixMode*>(this) : NULL); +} + +inline ListModeBase* ModeHandler::IsListModeBase() +{ + return (this->type_id == MC_LIST ? reinterpret_cast<ListModeBase*>(this) : NULL); +} + +inline const ListModeBase* ModeHandler::IsListModeBase() const +{ + return (this->type_id == MC_LIST ? reinterpret_cast<const ListModeBase*>(this) : NULL); +} + +inline ParamModeBase* ModeHandler::IsParameterMode() +{ + return (this->type_id == MC_PARAM ? reinterpret_cast<ParamModeBase*>(this) : NULL); +} + +inline const ParamModeBase* ModeHandler::IsParameterMode() const +{ + return (this->type_id == MC_PARAM ? reinterpret_cast<const ParamModeBase*>(this) : NULL); +} diff --git a/include/modechange.h b/include/modechange.h new file mode 100644 index 000000000..9ec105e73 --- /dev/null +++ b/include/modechange.h @@ -0,0 +1,127 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com> + * + * 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/>. + */ + + +#pragma once + +namespace Modes +{ + struct Change; + class ChangeList; +} + +/** A single mode to be changed + */ +struct Modes::Change +{ + bool adding; + ModeHandler* mh; + std::string param; + + /** + * @param handler Mode handler + * @param add True if this mode is being set, false if removed + * @param parameter Mode parameter + */ + Change(ModeHandler* handler, bool add, const std::string& parameter) + : adding(add) + , mh(handler) + , param(parameter) + { + } +}; + +/** A list of mode changes that can be applied on a Channel or User + */ +class Modes::ChangeList +{ + public: + typedef std::vector<Change> List; + + /** Add a new mode to be changed to this ChangeList + * @param change Mode change to add + */ + void push(const Modes::Change& change) + { + items.push_back(change); + } + + /** Insert multiple mode changes to the ChangeList + * @param first Iterator to the first change to insert + * @param last Iterator to the first change to not insert + */ + void push(List::const_iterator first, List::const_iterator last) + { + items.insert(items.end(), first, last); + } + + /** Add a new mode to be changed to this ChangeList + * @param mh Mode handler + * @param adding True if this mode is being set, false if removed + * @param param Mode parameter + */ + void push(ModeHandler* mh, bool adding, const std::string& param = std::string()) + { + items.push_back(Change(mh, adding, param)); + } + + /** Add a new mode to this ChangeList which will be set on the target + * @param mh Mode handler + * @param param Mode parameter + */ + void push_add(ModeHandler* mh, const std::string& param = std::string()) + { + push(mh, true, param); + } + + /** Add a new mode to this ChangeList which will be unset from the target + * @param mh Mode handler + * @param param Mode parameter + */ + void push_remove(ModeHandler* mh, const std::string& param = std::string()) + { + push(mh, false, param); + } + + /** Remove all mode changes from this stack + */ + void clear() { items.clear(); } + + /** Checks whether the ChangeList is empty, equivalent to (size() != 0). + * @return True if the ChangeList is empty, false otherwise. + */ + bool empty() const { return items.empty(); } + + /** Get number of mode changes in this ChangeList + * @return Number of mode changes in this ChangeList + */ + List::size_type size() const { return items.size(); } + + /** Get the list of mode changes in this ChangeList + * @return List of modes added to this ChangeList + */ + const List& getlist() const { return items; } + + /** Get the list of mode changes in this ChangeList + * @return List of modes added to this ChangeList + */ + List& getlist() { return items; } + + private: + List items; +}; diff --git a/include/modes/cmode_b.h b/include/modes/cmode_b.h deleted file mode 100644 index afd5cd13b..000000000 --- a/include/modes/cmode_b.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * InspIRCd -- Internet Relay Chat Daemon - * - * Copyright (C) 2007 Dennis Friis <peavey@inspircd.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 "mode.h" -#include "channels.h" - -class InspIRCd; - -/** Channel mode +b - */ -class ModeChannelBan : public ModeHandler -{ - private: - BanItem b; - public: - ModeChannelBan(); - ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding); - std::string& AddBan(User *user,std::string& dest,Channel *chan,int status); - std::string& DelBan(User *user,std::string& dest,Channel *chan,int status); - void DisplayList(User* user, Channel* channel); - void DisplayEmptyList(User* user, Channel* channel); - void RemoveMode(User* user, irc::modestacker* stack = NULL); - void RemoveMode(Channel* channel, irc::modestacker* stack = NULL); -}; - diff --git a/include/modes/cmode_k.h b/include/modes/cmode_k.h deleted file mode 100644 index 000667f72..000000000 --- a/include/modes/cmode_k.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * InspIRCd -- Internet Relay Chat Daemon - * - * Copyright (C) 2007 Dennis Friis <peavey@inspircd.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 "mode.h" - -class InspIRCd; - -/** Channel mode +k - */ -class ModeChannelKey : public ModeHandler -{ - public: - ModeChannelKey(); - ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding); - void RemoveMode(Channel* channel, irc::modestacker* stack = NULL); - void RemoveMode(User* user, irc::modestacker* stack = NULL); -}; diff --git a/include/modes/cmode_l.h b/include/modes/cmode_l.h deleted file mode 100644 index 3018a0d67..000000000 --- a/include/modes/cmode_l.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * InspIRCd -- Internet Relay Chat Daemon - * - * Copyright (C) 2007 Dennis Friis <peavey@inspircd.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 "mode.h" - -/** Channel mode +l - */ -class ModeChannelLimit : public ParamChannelModeHandler -{ - public: - ModeChannelLimit(); - bool ParamValidate(std::string& parameter); - bool ResolveModeConflict(std::string &their_param, const std::string &our_param, Channel* channel); -}; diff --git a/include/modes/cmode_v.h b/include/modes/cmode_v.h deleted file mode 100644 index ab037f33f..000000000 --- a/include/modes/cmode_v.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * InspIRCd -- Internet Relay Chat Daemon - * - * Copyright (C) 2007 Dennis Friis <peavey@inspircd.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 "mode.h" -#include "channels.h" - -class InspIRCd; - -/** Channel mode +v - */ -class ModeChannelVoice : public ModeHandler -{ - private: - public: - ModeChannelVoice(); - ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding); - unsigned int GetPrefixRank(); - void RemoveMode(User* user, irc::modestacker* stack = NULL); - void RemoveMode(Channel* channel, irc::modestacker* stack = NULL); -}; - diff --git a/include/modes/simplemodes.h b/include/modes/simplemodes.h deleted file mode 100644 index 661bba400..000000000 --- a/include/modes/simplemodes.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * InspIRCd -- Internet Relay Chat Daemon - * - * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org> - * Copyright (C) 2006 Craig Edwards <craigedwards@brainbox.cc> - * - * 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 "mode.h" - -/** Channel mode +i - */ -class ModeChannelInviteOnly : public SimpleChannelModeHandler -{ - public: - ModeChannelInviteOnly() : SimpleChannelModeHandler(NULL, "inviteonly", 'i') - { - } -}; - -/** Channel mode +m - */ -class ModeChannelModerated : public SimpleChannelModeHandler -{ - public: - ModeChannelModerated() : SimpleChannelModeHandler(NULL, "moderated", 'm') - { - } -}; - -/** Channel mode +n - */ -class ModeChannelNoExternal : public SimpleChannelModeHandler -{ - public: - ModeChannelNoExternal() : SimpleChannelModeHandler(NULL, "noextmsg", 'n') - { - } -}; - -/** Channel mode +p - */ -class ModeChannelPrivate : public SimpleChannelModeHandler -{ - public: - ModeChannelPrivate() : SimpleChannelModeHandler(NULL, "private", 'p') - { - } -}; - -/** Channel mode +s - */ -class ModeChannelSecret : public SimpleChannelModeHandler -{ - public: - ModeChannelSecret() : SimpleChannelModeHandler(NULL, "secret", 's') - { - } -}; - -/** Channel mode +t - */ -class ModeChannelTopicOps : public SimpleChannelModeHandler -{ - public: - ModeChannelTopicOps() : SimpleChannelModeHandler(NULL, "topiclock", 't') - { - } -}; - -/** User mode +i - */ -class ModeUserInvisible : public SimpleUserModeHandler -{ - public: - ModeUserInvisible() : SimpleUserModeHandler(NULL, "invisible", 'i') - { - } -}; - -/** User mode +w - */ -class ModeUserWallops : public SimpleUserModeHandler -{ - public: - ModeUserWallops() : SimpleUserModeHandler(NULL, "wallops", 'w') - { - } -}; diff --git a/include/modes/umode_s.h b/include/modes/umode_s.h deleted file mode 100644 index 8ac8fa31a..000000000 --- a/include/modes/umode_s.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * InspIRCd -- Internet Relay Chat Daemon - * - * Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net> - * Copyright (C) 2007 Dennis Friis <peavey@inspircd.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 "mode.h" - -class InspIRCd; - -/** User mode +n - */ -class ModeUserServerNoticeMask : public ModeHandler -{ - public: - ModeUserServerNoticeMask(); - ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding); - void OnParameterMissing(User* user, User* dest, Channel* channel); - std::string GetUserParameter(User* user); -}; diff --git a/include/modules.h b/include/modules.h index b16dcc49a..43ab2ae3a 100644 --- a/include/modules.h +++ b/include/modules.h @@ -23,8 +23,7 @@ */ -#ifndef MODULES_H -#define MODULES_H +#pragma once #include "dynamic.h" #include "base.h" @@ -35,33 +34,24 @@ #include <sstream> #include "timer.h" #include "mode.h" -#include "dns.h" -/** Used to define a set of behavior bits for a module - */ -enum ModuleFlags { - VF_NONE = 0, // module is not special at all - VF_STATIC = 1, // module is static, cannot be /unloadmodule'd - VF_VENDOR = 2, // module is a vendor module (came in the original tarball, not 3rd party) - VF_COMMON = 4, // module needs to be common on all servers in a network to link - VF_OPTCOMMON = 8, // module should be common on all servers for unsurprising behavior - VF_CORE = 16 // module is a core command, can be assumed loaded on all servers -}; +/** Used to specify the behaviour of a module. */ +enum ModuleFlags +{ + /** The module has no special attributes. */ + VF_NONE = 0, -/** Used to represent an event type, for user, channel or server - */ -enum TargetTypeFlags { - TYPE_USER = 1, - TYPE_CHANNEL, - TYPE_SERVER, - TYPE_OTHER -}; + /** The module is a coremod and can be assumed to be loaded on all servers. */ + VF_CORE = 1, -/** Used to represent wether a message was PRIVMSG or NOTICE - */ -enum MessageType { - MSG_PRIVMSG = 0, - MSG_NOTICE = 1 + /* The module is included with InspIRCd. */ + VF_VENDOR = 2, + + /** The module MUST be loaded on all servers on a network to link. */ + VF_COMMON = 4, + + /** The module SHOULD be loaded on all servers on a network for consistency. */ + VF_OPTCOMMON = 8 }; #define MOD_RES_ALLOW (ModResult(1)) @@ -109,35 +99,33 @@ struct ModResult { /** InspIRCd major version. * 1.2 -> 102; 2.1 -> 201; 2.12 -> 212 */ -#define INSPIRCD_VERSION_MAJ 200 +#define INSPIRCD_VERSION_MAJ 202 /** InspIRCd API version. * If you change any API elements, increment this value. This counter should be * reset whenever the major version is changed. Modules can use these two values * and numerical comparisons in preprocessor macros if they wish to support * multiple versions of InspIRCd in one file. */ -#define INSPIRCD_VERSION_API 11 +#define INSPIRCD_VERSION_API 1 /** * This #define allows us to call a method in all * loaded modules in a readable simple way, e.g.: - * 'FOREACH_MOD(I_OnConnect,OnConnect(user));' + * 'FOREACH_MOD(OnConnect,(user));' */ #define FOREACH_MOD(y,x) do { \ - EventHandlerIter safei; \ - for (EventHandlerIter _i = ServerInstance->Modules->EventHandlers[y].begin(); _i != ServerInstance->Modules->EventHandlers[y].end(); ) \ + const Module::List& _handlers = ServerInstance->Modules->EventHandlers[I_ ## y]; \ + for (Module::List::const_reverse_iterator _i = _handlers.rbegin(), _next; _i != _handlers.rend(); _i = _next) \ { \ - safei = _i; \ - ++safei; \ + _next = _i+1; \ try \ { \ - (*_i)->x ; \ + (*_i)->y x ; \ } \ catch (CoreException& modexcept) \ { \ - ServerInstance->Logs->Log("MODULE",DEFAULT,"Exception caught: %s",modexcept.GetReason()); \ + ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, "Exception caught: " + modexcept.GetReason()); \ } \ - _i = safei; \ } \ } while (0); @@ -149,21 +137,19 @@ struct ModResult { */ #define DO_EACH_HOOK(n,v,args) \ do { \ - EventHandlerIter iter_ ## n = ServerInstance->Modules->EventHandlers[I_ ## n].begin(); \ - while (iter_ ## n != ServerInstance->Modules->EventHandlers[I_ ## n].end()) \ + const Module::List& _handlers = ServerInstance->Modules->EventHandlers[I_ ## n]; \ + for (Module::List::const_reverse_iterator _i = _handlers.rbegin(), _next; _i != _handlers.rend(); _i = _next) \ { \ - Module* mod_ ## n = *iter_ ## n; \ - iter_ ## n ++; \ + _next = _i+1; \ try \ { \ - v = (mod_ ## n)->n args; + v = (*_i)->n args; #define WHILE_EACH_HOOK(n) \ } \ catch (CoreException& except_ ## n) \ { \ - ServerInstance->Logs->Log("MODULE",DEFAULT,"Exception caught: %s", (except_ ## n).GetReason()); \ - (void) mod_ ## n; /* catch mismatched pairs */ \ + ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, "Exception caught: " + (except_ ## n).GetReason()); \ } \ } \ } while(0) @@ -208,71 +194,6 @@ class CoreExport Version /** Complex version information, including linking compatability data */ Version(const std::string &desc, int flags, const std::string& linkdata); - - virtual ~Version() {} -}; - -/** The Request class is a unicast message directed at a given module. - * When this class is properly instantiated it may be sent to a module - * using the Send() method, which will call the given module's OnRequest - * method with this class as its parameter. - */ -class CoreExport Request : public classbase -{ - public: - /** This should be a null-terminated string identifying the type of request, - * all modules should define this and use it to determine the nature of the - * request before they attempt to cast the Request in any way. - */ - const char* const id; - /** This is a pointer to the sender of the message, which can be used to - * directly trigger events, or to create a reply. - */ - ModuleRef source; - /** The single destination of the Request - */ - ModuleRef dest; - - /** Create a new Request - * This is for the 'new' way of defining a subclass - * of Request and defining it in a common header, - * passing an object of your Request subclass through - * as a Request* and using the ID string to determine - * what to cast it back to and the other end. - */ - Request(Module* src, Module* dst, const char* idstr); - /** Send the Request. - */ - void Send(); -}; - - -/** The Event class is a unicast message directed at all modules. - * When the class is properly instantiated it may be sent to all modules - * using the Send() method, which will trigger the OnEvent method in - * all modules passing the object as its parameter. - */ -class CoreExport Event : public classbase -{ - public: - /** This is a pointer to the sender of the message, which can be used to - * directly trigger events, or to create a reply. - */ - ModuleRef source; - /** The event identifier. - * This is arbitary text which should be used to distinguish - * one type of event from another. - */ - const std::string id; - - /** Create a new Event - */ - Event(Module* src, const std::string &eventid); - /** Send the Event. - * The return result of an Event::Send() will always be NULL as - * no replies are expected. - */ - void Send(); }; class CoreExport DataProvider : public ServiceProvider @@ -282,38 +203,6 @@ class CoreExport DataProvider : public ServiceProvider : ServiceProvider(Creator, Name, SERVICE_DATA) {} }; -class CoreExport dynamic_reference_base : public interfacebase -{ - private: - std::string name; - protected: - DataProvider* value; - public: - ModuleRef creator; - dynamic_reference_base(Module* Creator, const std::string& Name); - ~dynamic_reference_base(); - inline void ClearCache() { value = NULL; } - inline const std::string& GetProvider() { return name; } - void SetProvider(const std::string& newname); - void lookup(); - operator bool(); - static void reset_all(); -}; - -template<typename T> -class dynamic_reference : public dynamic_reference_base -{ - public: - dynamic_reference(Module* Creator, const std::string& Name) - : dynamic_reference_base(Creator, Name) {} - inline T* operator->() - { - if (!value) - lookup(); - return static_cast<T*>(value); - } -}; - /** Priority types which can be used by Module::Prioritize() */ enum Priority { PRIORITY_FIRST, PRIORITY_LAST, PRIORITY_BEFORE, PRIORITY_AFTER }; @@ -322,23 +211,23 @@ enum Priority { PRIORITY_FIRST, PRIORITY_LAST, PRIORITY_BEFORE, PRIORITY_AFTER } */ enum Implementation { - I_BEGIN, - I_OnUserConnect, I_OnUserQuit, I_OnUserDisconnect, I_OnUserJoin, I_OnUserPart, I_OnRehash, - I_OnSendSnotice, I_OnUserPreJoin, I_OnUserPreKick, I_OnUserKick, I_OnOper, I_OnInfo, I_OnWhois, - I_OnUserPreInvite, I_OnUserInvite, I_OnUserPreMessage, I_OnUserPreNotice, I_OnUserPreNick, - I_OnUserMessage, I_OnUserNotice, I_OnMode, I_OnGetServerDescription, I_OnSyncUser, - I_OnSyncChannel, I_OnDecodeMetaData, I_OnWallops, I_OnAcceptConnection, I_OnUserInit, - I_OnChangeHost, I_OnChangeName, I_OnAddLine, I_OnDelLine, I_OnExpireLine, - I_OnUserPostNick, I_OnPreMode, I_On005Numeric, I_OnKill, I_OnRemoteKill, I_OnLoadModule, + I_OnUserConnect, I_OnUserQuit, I_OnUserDisconnect, I_OnUserJoin, I_OnUserPart, + I_OnSendSnotice, I_OnUserPreJoin, I_OnUserPreKick, I_OnUserKick, I_OnOper, + I_OnUserPreInvite, I_OnUserInvite, I_OnUserPreMessage, I_OnUserPreNick, + I_OnUserPostMessage, I_OnUserMessageBlocked, I_OnMode, + I_OnDecodeMetaData, I_OnAcceptConnection, I_OnUserInit, + I_OnChangeHost, I_OnChangeRealName, I_OnAddLine, I_OnDelLine, I_OnExpireLine, + I_OnUserPostNick, I_OnPreMode, I_On005Numeric, I_OnKill, I_OnLoadModule, I_OnUnloadModule, I_OnBackgroundTimer, I_OnPreCommand, I_OnCheckReady, I_OnCheckInvite, I_OnRawMode, I_OnCheckKey, I_OnCheckLimit, I_OnCheckBan, I_OnCheckChannelBan, I_OnExtBanCheck, - I_OnStats, I_OnChangeLocalUserHost, I_OnPreTopicChange, I_OnPostDeoper, - I_OnPostTopicChange, I_OnEvent, I_OnGlobalOper, I_OnPostConnect, I_OnAddBan, - I_OnDelBan, I_OnChangeLocalUserGECOS, I_OnUserRegister, I_OnChannelPreDelete, I_OnChannelDelete, - I_OnPostOper, I_OnSyncNetwork, I_OnSetAway, I_OnPostCommand, I_OnPostJoin, - I_OnWhoisLine, I_OnBuildNeighborList, I_OnGarbageCollect, I_OnSetConnectClass, - I_OnText, I_OnPassCompare, I_OnRunTestSuite, I_OnNamesListItem, I_OnNumeric, I_OnHookIO, + I_OnPreChangeHost, I_OnPreTopicChange, + I_OnPostTopicChange, I_OnPostConnect, I_OnPostDeoper, + I_OnPreChangeRealName, I_OnUserRegister, I_OnChannelPreDelete, I_OnChannelDelete, + I_OnPostOper, I_OnPostCommand, I_OnPostJoin, + I_OnBuildNeighborList, I_OnGarbageCollect, I_OnSetConnectClass, + I_OnUserMessage, I_OnPassCompare, I_OnNamesListItem, I_OnNumeric, I_OnPreRehash, I_OnModuleRehash, I_OnSendWhoLine, I_OnChangeIdent, I_OnSetUserIP, + I_OnServiceAdd, I_OnServiceDel, I_OnUserWrite, I_END }; @@ -349,10 +238,19 @@ enum Implementation */ class CoreExport Module : public classbase, public usecountbase { + /** Detach an event from this module + * @param i Event type to detach + */ + void DetachEvent(Implementation i); + public: + /** A list of modules. */ + typedef std::vector<Module*> List; + /** File that this module was loaded from */ std::string ModuleSourceFile; + /** Reference to the dlopen() value */ DLLManager* ModuleDLLManager; @@ -376,7 +274,7 @@ class CoreExport Module : public classbase, public usecountbase /** Clean up prior to destruction * If you override, you must call this AFTER your module's cleanup */ - virtual CullResult cull(); + CullResult cull() CXX11_OVERRIDE; /** Default destructor. * destroys a module class @@ -387,6 +285,13 @@ class CoreExport Module : public classbase, public usecountbase { } + /** This method is called when you should reload module specific configuration: + * on boot, on a /REHASH and on module load. + * @param status The current status, can be inspected for more information; + * also used for reporting configuration errors and warnings. + */ + virtual void ReadConfig(ConfigStatus& status); + /** Returns the version number of a Module. * The method should return a Version object with its version information assigned via * Version::Version @@ -477,14 +382,6 @@ class CoreExport Module : public classbase, public usecountbase */ virtual void OnModuleRehash(User* user, const std::string ¶meter); - /** Called on rehash. - * This method is called after a rehash has completed. You should use it to reload any module - * configuration from the main configuration file. - * @param user The user that performed the rehash, if it was initiated by a user and that user - * is still connected. - */ - virtual void OnRehash(User* user); - /** Called whenever a snotice is about to be sent to a snomask. * snomask and type may both be modified; the message may not. * @param snomask The snomask the message is going to (e.g. 'A') @@ -514,7 +411,7 @@ class CoreExport Module : public classbase, public usecountbase * @param keygiven The key given to join the channel, or an empty string if none was provided * @return 1 To prevent the join, 0 to allow it. */ - virtual ModResult OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs, const std::string &keygiven); + virtual ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven); /** Called whenever a user is about to be kicked. * Returning a value of 1 from this function stops the process immediately, causing no @@ -560,26 +457,6 @@ class CoreExport Module : public classbase, public usecountbase */ virtual void OnPostDeoper(User* user); - /** Called whenever a user types /INFO. - * The User will contain the information of the user who typed the command. Modules may use this - * method to output their own credits in /INFO (which is the ircd's version of an about box). - * It is purposefully not possible to modify any info that has already been output, or halt the list. - * You must write a 371 numeric to the user, containing your info in the following format: - * - * <nick> :information here - * - * @param user The user issuing /INFO - */ - virtual void OnInfo(User* user); - - /** Called whenever a /WHOIS is performed on a local user. - * The source parameter contains the details of the user who issued the WHOIS command, and - * the dest parameter contains the information of the user they are whoising. - * @param source The user issuing the WHOIS command - * @param dest The user who is being WHOISed - */ - virtual void OnWhois(User* source, User* dest); - /** Called whenever a user is about to invite another user into a channel, before any processing is done. * Returning 1 from this function stops the process immediately, causing no * output to be sent to the user by the core. If you do this you must produce your own numerics, @@ -599,47 +476,21 @@ class CoreExport Module : public classbase, public usecountbase * @param dest The user being invited * @param channel The channel the user is being invited to * @param timeout The time the invite will expire (0 == never) + * @param notifyrank Rank required to get an invite announcement (if enabled) + * @param notifyexcepts List of users to not send the default NOTICE invite announcement to */ - virtual void OnUserInvite(User* source,User* dest,Channel* channel, time_t timeout); + virtual void OnUserInvite(User* source, User* dest, Channel* channel, time_t timeout, unsigned int notifyrank, CUList& notifyexcepts); - /** Called whenever a user is about to PRIVMSG A user or a channel, before any processing is done. - * Returning any nonzero value from this function stops the process immediately, causing no - * output to be sent to the user by the core. If you do this you must produce your own numerics, - * notices etc. This is useful for modules which may want to filter or redirect messages. - * target_type can be one of TYPE_USER or TYPE_CHANNEL. If the target_type value is a user, - * you must cast dest to a User* otherwise you must cast it to a Channel*, this is the details - * of where the message is destined to be sent. - * @param user The user sending the message - * @param dest The target of the message (Channel* or User*) - * @param target_type The type of target (TYPE_USER or TYPE_CHANNEL) - * @param text Changeable text being sent by the user - * @param status The status being used, e.g. PRIVMSG @#chan has status== '@', 0 to send to everyone. - * @param exempt_list A list of users not to send to. For channel messages, this will usually contain just the sender. - * It will be ignored for private messages. - * @return 1 to deny the message, 0 to allow it - */ - virtual ModResult OnUserPreMessage(User* user,void* dest,int target_type, std::string &text,char status, CUList &exempt_list); - - /** Called whenever a user is about to NOTICE A user or a channel, before any processing is done. - * Returning any nonzero value from this function stops the process immediately, causing no - * output to be sent to the user by the core. If you do this you must produce your own numerics, - * notices etc. This is useful for modules which may want to filter or redirect messages. - * target_type can be one of TYPE_USER or TYPE_CHANNEL. If the target_type value is a user, - * you must cast dest to a User* otherwise you must cast it to a Channel*, this is the details - * of where the message is destined to be sent. - * You may alter the message text as you wish before relinquishing control to the next module - * in the chain, and if no other modules block the text this altered form of the text will be sent out - * to the user and possibly to other servers. - * @param user The user sending the message - * @param dest The target of the message (Channel* or User*) - * @param target_type The type of target (TYPE_USER or TYPE_CHANNEL) - * @param text Changeable text being sent by the user - * @param status The status being used, e.g. PRIVMSG @#chan has status== '@', 0 to send to everyone. - * @param exempt_list A list of users not to send to. For channel notices, this will usually contain just the sender. - * It will be ignored for private notices. - * @return 1 to deny the NOTICE, 0 to allow it - */ - virtual ModResult OnUserPreNotice(User* user,void* dest,int target_type, std::string &text,char status, CUList &exempt_list); + /** Called before a user sends a message to a channel, a user, or a server glob mask. + * @param user The user sending the message. + * @param target The target of the message. This can either be a channel, a user, or a server + * glob mask. + * @param details Details about the message such as the message text and type. See the + * MessageDetails class for more information. + * @return MOD_RES_ALLOW to explicitly allow the message, MOD_RES_DENY to explicitly deny the + * message, or MOD_RES_PASSTHRU to let another module handle the event. + */ + virtual ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details); /** Called when sending a message to all "neighbors" of a given user - * that is, all users that share a common channel. This is used in @@ -650,168 +501,65 @@ class CoreExport Module : public classbase, public usecountbase * * Set exceptions[user] = true to include, exceptions[user] = false to exclude */ - virtual void OnBuildNeighborList(User* source, UserChanList &include_c, std::map<User*,bool> &exceptions); + virtual void OnBuildNeighborList(User* source, IncludeChanList& include_c, std::map<User*, bool>& exceptions); - /** Called before any nickchange, local or remote. This can be used to implement Q-lines etc. - * Please note that although you can see remote nickchanges through this function, you should - * NOT make any changes to the User if the user is a remote user as this may cause a desnyc. - * check user->server before taking any action (including returning nonzero from the method). + /** Called before local nickname changes. This can be used to implement Q-lines etc. * If your method returns nonzero, the nickchange is silently forbidden, and it is down to your * module to generate some meaninful output. * @param user The username changing their nick * @param newnick Their new nickname * @return 1 to deny the change, 0 to allow */ - virtual ModResult OnUserPreNick(User* user, const std::string &newnick); - - /** Called after any PRIVMSG sent from a user. - * The dest variable contains a User* if target_type is TYPE_USER and a Channel* - * if target_type is TYPE_CHANNEL. - * @param user The user sending the message - * @param dest The target of the message - * @param target_type The type of target (TYPE_USER or TYPE_CHANNEL) - * @param text the text being sent by the user - * @param status The status being used, e.g. PRIVMSG @#chan has status== '@', 0 to send to everyone. - * @param exempt_list A list of users to not send to. - */ - virtual void OnUserMessage(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list); - - /** Called after any NOTICE sent from a user. - * The dest variable contains a User* if target_type is TYPE_USER and a Channel* - * if target_type is TYPE_CHANNEL. - * @param user The user sending the message - * @param dest The target of the message - * @param target_type The type of target (TYPE_USER or TYPE_CHANNEL) - * @param text the text being sent by the user - * @param status The status being used, e.g. NOTICE @#chan has status== '@', 0 to send to everyone. - * @param exempt_list A list of users to not send to. - */ - virtual void OnUserNotice(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list); - - /** Called immediately before any NOTICE or PRIVMSG sent from a user, local or remote. - * The dest variable contains a User* if target_type is TYPE_USER and a Channel* - * if target_type is TYPE_CHANNEL. - * The difference between this event and OnUserPreNotice/OnUserPreMessage is that delivery is gauranteed, - * the message has already been vetted. In the case of the other two methods, a later module may stop your - * message. This also differs from OnUserMessage which occurs AFTER the message has been sent. - * @param user The user sending the message - * @param dest The target of the message - * @param target_type The type of target (TYPE_USER or TYPE_CHANNEL) - * @param text the text being sent by the user - * @param status The status being used, e.g. NOTICE @#chan has status== '@', 0 to send to everyone. - * @param exempt_list A list of users not to send to. For channel messages, this will usually contain just the sender. - */ - virtual void OnText(User* user, void* dest, int target_type, const std::string &text, char status, CUList &exempt_list); + virtual ModResult OnUserPreNick(LocalUser* user, const std::string& newnick); - /** Called after every MODE command sent from a user - * The dest variable contains a User* if target_type is TYPE_USER and a Channel* - * if target_type is TYPE_CHANNEL. The text variable contains the remainder of the - * mode string after the target, e.g. "+wsi" or "+ooo nick1 nick2 nick3". - * @param user The user sending the MODEs - * @param dest The target of the modes (User* or Channel*) - * @param target_type The type of target (TYPE_USER or TYPE_CHANNEL) - * @param text The actual modes and their parameters if any - * @param translate The translation types of the mode parameters - */ - virtual void OnMode(User* user, void* dest, int target_type, const std::vector<std::string> &text, const std::vector<TranslateType> &translate); - - /** Allows modules to alter or create server descriptions - * Whenever a module requires a server description, for example for display in - * WHOIS, this function is called in all modules. You may change or define the - * description given in std::string &description. If you do, this description - * will be shown in the WHOIS fields. - * @param servername The servername being searched for - * @param description Alterable server description for this server - */ - virtual void OnGetServerDescription(const std::string &servername,std::string &description); - - /** Allows modules to synchronize data which relates to users during a netburst. - * When this function is called, it will be called from the module which implements - * the linking protocol. This currently is m_spanningtree.so. A pointer to this module - * is given in Module* proto, so that you may call its methods such as ProtoSendMode - * (see below). This function will be called for every user visible on your side - * of the burst, allowing you to for example set modes, etc. Do not use this call to - * synchronize data which you have stored using class Extensible -- There is a specialist - * function OnSyncUserMetaData and OnSyncChannelMetaData for this! - * @param user The user being syncronized - * @param proto A pointer to the module handling network protocol - * @param opaque An opaque pointer set by the protocol module, should not be modified! - */ - virtual void OnSyncUser(User* user, Module* proto, void* opaque); - - /** Allows modules to synchronize data which relates to channels during a netburst. - * When this function is called, it will be called from the module which implements - * the linking protocol. This currently is m_spanningtree.so. A pointer to this module - * is given in Module* proto, so that you may call its methods such as ProtoSendMode - * (see below). This function will be called for every user visible on your side - * of the burst, allowing you to for example set modes, etc. - * - * For a good example of how to use this function, please see src/modules/m_chanprotect.cpp - * - * @param chan The channel being syncronized - * @param proto A pointer to the module handling network protocol - * @param opaque An opaque pointer set by the protocol module, should not be modified! + /** Called immediately after a user sends a message to a channel, a user, or a server glob mask. + * @param user The user sending the message. + * @param target The target of the message. This can either be a channel, a user, or a server + * glob mask. + * @param details Details about the message such as the message text and type. See the + * MessageDetails class for more information. */ - virtual void OnSyncChannel(Channel* chan, Module* proto, void* opaque); + virtual void OnUserPostMessage(User* user, const MessageTarget& target, const MessageDetails& details); - /* Allows modules to syncronize metadata not related to users or channels, over the network during a netburst. - * Whenever the linking module wants to send out data, but doesnt know what the data - * represents (e.g. it is Extensible metadata, added to a User or Channel by a module) then - * this method is called. You should use the ProtoSendMetaData function after you've - * correctly decided how the data should be represented, to send the metadata on its way if - * if it belongs to your module. - * @param proto A pointer to the module handling network protocol - * @param opaque An opaque pointer set by the protocol module, should not be modified! - * @param displayable If this value is true, the data is going to be displayed to a user, - * and not sent across the network. Use this to determine wether or not to show sensitive data. + /** Called immediately before a user sends a message to a channel, a user, or a server glob mask. + * @param user The user sending the message. + * @param target The target of the message. This can either be a channel, a user, or a server + * glob mask. + * @param details Details about the message such as the message text and type. See the + * MessageDetails class for more information. */ - virtual void OnSyncNetwork(Module* proto, void* opaque); + virtual void OnUserMessage(User* user, const MessageTarget& target, const MessageDetails& details); + + /** Called when a message sent by a user to a channel, a user, or a server glob mask is blocked. + * @param user The user sending the message. + * @param target The target of the message. This can either be a channel, a user, or a server + * glob mask. + * @param details Details about the message such as the message text and type. See the + * MessageDetails class for more information. + */ + virtual void OnUserMessageBlocked(User* user, const MessageTarget& target, const MessageDetails& details); + + /** Called after every MODE command sent from a user + * Either the usertarget or the chantarget variable contains the target of the modes, + * the actual target will have a non-NULL pointer. + * All changed modes are available in the changelist object. + * @param user The user sending the MODEs + * @param usertarget The target user of the modes, NULL if the target is a channel + * @param chantarget The target channel of the modes, NULL if the target is a user + * @param changelist The changed modes. + * @param processflags Flags passed to ModeParser::Process(), see ModeParser::ModeProcessFlags + * for the possible flags. + */ + virtual void OnMode(User* user, User* usertarget, Channel* chantarget, const Modes::ChangeList& changelist, ModeParser::ModeProcessFlag processflags); /** Allows module data, sent via ProtoSendMetaData, to be decoded again by a receiving module. * Please see src/modules/m_swhois.cpp for a working example of how to use this method call. * @param target The Channel* or User* that data should be added to * @param extname The extension name which is being sent - * @param extdata The extension data, encoded at the other end by an identical module through OnSyncChannelMetaData or OnSyncUserMetaData + * @param extdata The extension data, encoded at the other end by an identical module */ virtual void OnDecodeMetaData(Extensible* target, const std::string &extname, const std::string &extdata); - /** Implemented by modules which provide the ability to link servers. - * These modules will implement this method, which allows transparent sending of servermodes - * down the network link as a broadcast, without a module calling it having to know the format - * of the MODE command before the actual mode string. - * - * More documentation to follow soon. Please see src/modules/m_chanprotect.cpp for examples - * of how to use this function. - * - * @param opaque An opaque pointer set by the protocol module, should not be modified! - * @param target_type The type of item to decode data for, TYPE_USER or TYPE_CHANNEL - * @param target The Channel* or User* that modes should be sent for - * @param modeline The modes and parameters to be sent - * @param translate The translation types of the mode parameters - */ - virtual void ProtoSendMode(void* opaque, TargetTypeFlags target_type, void* target, const std::vector<std::string> &modeline, const std::vector<TranslateType> &translate); - - /** Implemented by modules which provide the ability to link servers. - * These modules will implement this method, which allows metadata (extra data added to - * user and channel records using class Extensible, Extensible::Extend, etc) to be sent - * to other servers on a netburst and decoded at the other end by the same module on a - * different server. - * - * More documentation to follow soon. Please see src/modules/m_swhois.cpp for example of - * how to use this function. - * @param opaque An opaque pointer set by the protocol module, should not be modified! - * @param target The Channel* or User* that metadata should be sent for - * @param extname The extension name to send metadata for - * @param extdata Encoded data for this extension name, which will be encoded at the oppsite end by an identical module using OnDecodeMetaData - */ - virtual void ProtoSendMetaData(void* opaque, Extensible* target, const std::string &extname, const std::string &extdata); - - /** Called after every WALLOPS command. - * @param user The user sending the WALLOPS - * @param text The content of the WALLOPS message - */ - virtual void OnWallops(User* user, const std::string &text); - /** Called whenever a user's hostname is changed. * This event triggers after the host has been set. * @param user The user whos host is being changed @@ -819,12 +567,12 @@ class CoreExport Module : public classbase, public usecountbase */ virtual void OnChangeHost(User* user, const std::string &newhost); - /** Called whenever a user's GECOS (realname) is changed. + /** Called whenever a user's real name is changed. * This event triggers after the name has been set. - * @param user The user who's GECOS is being changed - * @param gecos The new GECOS being set on the user + * @param user The user who's real name is being changed + * @param name The new real name being set on the user */ - virtual void OnChangeName(User* user, const std::string &gecos); + virtual void OnChangeRealName(User* user, const std::string& real); /** Called whenever a user's IDENT is changed. * This event triggers after the name has been set. @@ -853,16 +601,15 @@ class CoreExport Module : public classbase, public usecountbase */ virtual void OnExpireLine(XLine *line); - /** Called before your module is unloaded to clean up Extensibles. - * This method is called once for every user and channel on the network, - * so that when your module unloads it may clear up any remaining data - * in the form of Extensibles added using Extensible::Extend(). - * If the target_type variable is TYPE_USER, then void* item refers to - * a User*, otherwise it refers to a Channel*. - * @param target_type The type of item being cleaned - * @param item A pointer to the item's class + /** Called before the module is unloaded to clean up extensibles. + * This method is called once for every channel, membership, and user. + * so that you can clear up any data relating to the specified extensible. + * @param type The type of extensible being cleaned up. If this is EXT_CHANNEL + * then item is a Channel*, EXT_MEMBERSHIP then item is a Membership*, + * and EXT_USER then item is a User*. + * @param item A pointer to the extensible which is being cleaned up. */ - virtual void OnCleanup(int target_type, void* item); + virtual void OnCleanup(ExtensionItem::ExtensibleType type, Extensible* item); /** Called after any nickchange, local or remote. This can be used to track users after nickchanges * have been applied. Please note that although you can see remote nickchanges through this function, you should @@ -875,7 +622,7 @@ class CoreExport Module : public classbase, public usecountbase */ virtual void OnUserPostNick(User* user, const std::string &oldnick); - /** Called before any mode change, to allow a single access check for + /** Called before a mode change via the MODE command, to allow a single access check for * a full mode change (use OnRawMode to check individual modes) * * Returning MOD_RES_ALLOW will skip prefix level checks, but can be overridden by @@ -884,15 +631,15 @@ class CoreExport Module : public classbase, public usecountbase * @param source the user making the mode change * @param dest the user destination of the umode change (NULL if a channel mode) * @param channel the channel destination of the mode change - * @param parameters raw mode parameters; parameters[0] is the user/channel being changed + * @param modes Modes being changed, can be edited */ - virtual ModResult OnPreMode(User* source, User* dest, Channel* channel, const std::vector<std::string>& parameters); + virtual ModResult OnPreMode(User* source, User* dest, Channel* channel, Modes::ChangeList& modes); /** Called when a 005 numeric is about to be output. * The module should modify the 005 numeric if needed to indicate its features. - * @param output The 005 string to be modified if neccessary. - */ - virtual void On005Numeric(std::string &output); + * @param tokens The 005 map to be modified if neccessary. + */ + virtual void On005Numeric(std::map<std::string, std::string>& tokens); /** Called when a client is disconnected by KILL. * If a client is killed by a server, e.g. a nickname collision or protocol error, @@ -909,14 +656,6 @@ class CoreExport Module : public classbase, public usecountbase */ virtual ModResult OnKill(User* source, User* dest, const std::string &reason); - /** Called when an oper wants to disconnect a remote user via KILL - * @param source The user sending the KILL - * @param dest The user being killed - * @param reason The kill reason - * @param operreason The oper kill reason - */ - virtual void OnRemoteKill(User* source, User* dest, const std::string &reason, const std::string &operreason); - /** Called whenever a module is loaded. * mod will contain a pointer to the module, and string will contain its name, * for example m_widgets.so. This function is primary for dependency checking, @@ -965,10 +704,9 @@ class CoreExport Module : public classbase, public usecountbase * @param user the user issuing the command * @param validated True if the command has passed all checks, e.g. it is recognised, has enough parameters, the user has permission to execute it, etc. * You should only change the parameter list and command string if validated == false (e.g. before the command lookup occurs). - * @param original_line The entire original line as passed to the parser from the user * @return 1 to block the command, 0 to allow */ - virtual ModResult OnPreCommand(std::string &command, std::vector<std::string>& parameters, LocalUser *user, bool validated, const std::string &original_line); + virtual ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated); /** Called after any command has been executed. * This event occurs for all registered commands, wether they are registered in the core, @@ -979,9 +717,9 @@ class CoreExport Module : public classbase, public usecountbase * @param parameters An array of array of characters containing the parameters for the command * @param user the user issuing the command * @param result The return code given by the command handler, one of CMD_SUCCESS or CMD_FAILURE - * @param original_line The entire original line as passed to the parser from the user + * @param loop Whether the command is being called from LoopCall or directly. */ - virtual void OnPostCommand(const std::string &command, const std::vector<std::string>& parameters, LocalUser *user, CmdResult result, const std::string &original_line); + virtual void OnPostCommand(Command* command, const CommandBase::Params& parameters, LocalUser* user, CmdResult result, bool loop); /** Called when a user is first connecting, prior to starting DNS lookups, checking initial * connect class, or accepting any commands. @@ -1025,15 +763,14 @@ class CoreExport Module : public classbase, public usecountbase * Return 1 from this function to block the mode character from being processed entirely. * @param user The user who is sending the mode * @param chan The channel the mode is being sent to (or NULL if a usermode) - * @param mode The mode character being set + * @param mh The mode handler for the mode being changed * @param param The parameter for the mode or an empty string * @param adding true of the mode is being added, false if it is being removed - * @param pcnt The parameter count for the mode (0 or 1) * @return ACR_DENY to deny the mode, ACR_DEFAULT to do standard mode checking, and ACR_ALLOW * to skip all permission checking. Please note that for remote mode changes, your return value * will be ignored! */ - virtual ModResult OnRawMode(User* user, Channel* chan, const char mode, const std::string ¶m, bool adding, int pcnt); + virtual ModResult OnRawMode(User* user, Channel* chan, ModeHandler* mh, const std::string& param, bool adding); /** Called whenever a user joins a channel, to determine if key checks should go ahead or not. * This method will always be called for each join, wether or not the channel is actually +k, and @@ -1082,32 +819,21 @@ class CoreExport Module : public classbase, public usecountbase */ virtual ModResult OnExtBanCheck(User* user, Channel* chan, char type); - /** Called on all /STATS commands - * This method is triggered for all /STATS use, including stats symbols handled by the core. - * @param symbol the symbol provided to /STATS - * @param user the user issuing the /STATS command - * @param results A string_list to append results into. You should put all your results - * into this string_list, rather than displaying them directly, so that your handler will - * work when remote STATS queries are received. - * @return 1 to block the /STATS from being processed by the core, 0 to allow it - */ - virtual ModResult OnStats(char symbol, User* user, string_list &results); - /** Called whenever a change of a local users displayed host is attempted. * Return 1 to deny the host change, or 0 to allow it. * @param user The user whos host will be changed * @param newhost The new hostname * @return 1 to deny the host change, 0 to allow */ - virtual ModResult OnChangeLocalUserHost(LocalUser* user, const std::string &newhost); + virtual ModResult OnPreChangeHost(LocalUser* user, const std::string &newhost); - /** Called whenever a change of a local users GECOS (fullname field) is attempted. - * return 1 to deny the name change, or 0 to allow it. - * @param user The user whos GECOS will be changed - * @param newhost The new GECOS - * @return 1 to deny the GECOS change, 0 to allow + /** Called whenever a change of a local users real name is attempted. + * return MOD_RES_DENY to deny the name change, or MOD_RES_ALLOW to allow it. + * @param user The user whos real name will be changed + * @param newhost The new real name. + * @return MOD_RES_DENY to deny the real name change, MOD_RES_ALLOW to allow */ - virtual ModResult OnChangeLocalUserGECOS(LocalUser* user, const std::string &newhost); + virtual ModResult OnPreChangeRealName(LocalUser* user, const std::string &newhost); /** Called before a topic is changed. * Return 1 to deny the topic change, 0 to check details on the change, -1 to let it through with no checks @@ -1127,18 +853,6 @@ class CoreExport Module : public classbase, public usecountbase */ virtual void OnPostTopicChange(User* user, Channel* chan, const std::string &topic); - /** Called whenever an Event class is sent to all modules by another module. - * You should *always* check the value of Event::id to determine the event type. - * @param event The Event class being received - */ - virtual void OnEvent(Event& event); - - /** Called whenever a Request class is sent to your module by another module. - * The value of Request::id should be used to determine the type of request. - * @param request The Request class being received - */ - virtual void OnRequest(Request& request); - /** Called whenever a password check is to be made. Replaces the old OldOperCompare API. * The password field (from the config file) is in 'password' and is to be compared against * 'input'. This method allows for encryption of passwords (oper, connect:allow, die/restart, etc). @@ -1151,14 +865,6 @@ class CoreExport Module : public classbase, public usecountbase */ virtual ModResult OnPassCompare(Extensible* ex, const std::string &password, const std::string &input, const std::string& hashtype); - /** Called whenever a user is given usermode +o, anywhere on the network. - * You cannot override this and prevent it from happening as it is already happened and - * such a task must be performed by another server. You can however bounce modes by sending - * servermodes out to reverse mode changes. - * @param user The user who is opering - */ - virtual void OnGlobalOper(User* user); - /** Called after a user has fully connected and all modules have executed OnUserConnect * This event is informational only. You should not change any user information in this * event. To do so, use the OnUserConnect method to change the state of local users. @@ -1167,30 +873,6 @@ class CoreExport Module : public classbase, public usecountbase */ virtual void OnPostConnect(User* user); - /** Called whenever a ban is added to a channel's list. - * Return a non-zero value to 'eat' the mode change and prevent the ban from being added. - * @param source The user adding the ban - * @param channel The channel the ban is being added to - * @param banmask The ban mask being added - * @return 1 to block the ban, 0 to continue as normal - */ - virtual ModResult OnAddBan(User* source, Channel* channel,const std::string &banmask); - - /** Called whenever a ban is removed from a channel's list. - * Return a non-zero value to 'eat' the mode change and prevent the ban from being removed. - * @param source The user deleting the ban - * @param channel The channel the ban is being deleted from - * @param banmask The ban mask being deleted - * @return 1 to block the unban, 0 to continue as normal - */ - virtual ModResult OnDelBan(User* source, Channel* channel,const std::string &banmask); - - /** Called to install an I/O hook on an event handler - * @param user The socket to possibly install the I/O hook on - * @param via The port that the user connected on - */ - virtual void OnHookIO(StreamSocket* user, ListenSocket* via); - /** Called when a port accepts a connection * Return MOD_RES_ACCEPT if you have used the file descriptor. * @param fd The file descriptor returned from accept() @@ -1200,71 +882,6 @@ class CoreExport Module : public classbase, public usecountbase */ virtual ModResult OnAcceptConnection(int fd, ListenSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server); - /** Called immediately after any connection is accepted. This is intended for raw socket - * processing (e.g. modules which wrap the tcp connection within another library) and provides - * no information relating to a user record as the connection has not been assigned yet. - * There are no return values from this call as all modules get an opportunity if required to - * process the connection. - * @param sock The socket in question - * @param client The client IP address and port - * @param server The server IP address and port - */ - virtual void OnStreamSocketAccept(StreamSocket* sock, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server); - - /** - * Called when a hooked stream has data to write, or when the socket - * engine returns it as writable - * @param sock The socket in question - * @param sendq Data to send to the socket - * @return 1 if the sendq has been completely emptied, 0 if there is - * still data to send, and -1 if there was an error - */ - virtual int OnStreamSocketWrite(StreamSocket* sock, std::string& sendq); - - /** Called immediately before any socket is closed. When this event is called, shutdown() - * has not yet been called on the socket. - * @param sock The socket in question - */ - virtual void OnStreamSocketClose(StreamSocket* sock); - - /** Called immediately upon connection of an outbound BufferedSocket which has been hooked - * by a module. - * @param sock The socket in question - */ - virtual void OnStreamSocketConnect(StreamSocket* sock); - - /** - * Called when the stream socket has data to read - * @param sock The socket that is ready - * @param recvq The receive queue that new data should be appended to - * @return 1 if new data has been read, 0 if no new data is ready (but the - * socket is still connected), -1 if there was an error or close - */ - virtual int OnStreamSocketRead(StreamSocket* sock, std::string& recvq); - - /** Called whenever a user sets away or returns from being away. - * The away message is available as a parameter, but should not be modified. - * At this stage, it has already been copied into the user record. - * If awaymsg is empty, the user is returning from away. - * @param user The user setting away - * @param awaymsg The away message of the user, or empty if returning from away - * @return nonzero if the away message should be blocked - should ONLY be nonzero for LOCAL users (IS_LOCAL) (no output is returned by core) - */ - virtual ModResult OnSetAway(User* user, const std::string &awaymsg); - - /** Called whenever a line of WHOIS output is sent to a user. - * You may change the numeric and the text of the output by changing - * the values numeric and text, but you cannot change the user the - * numeric is sent to. You may however change the user's User values. - * @param user The user the numeric is being sent to - * @param dest The user being WHOISed - * @param numeric The numeric of the line being sent - * @param text The text of the numeric, including any parameters - * @return nonzero to drop the line completely so that the user does not - * receive it, or zero to allow the line to be sent. - */ - virtual ModResult OnWhoisLine(User* user, User* dest, int &numeric, std::string &text); - /** Called at intervals for modules to garbage-collect any hashes etc. * Certain data types such as hash_map 'leak' buckets, which must be * tidied up and freed by copying into a new item every so often. This @@ -1278,223 +895,69 @@ class CoreExport Module : public classbase, public usecountbase */ virtual ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass); +#ifdef INSPIRCD_ENABLE_TESTSUITE /** Add test suite hooks here. These are used for testing functionality of a module * via the --testsuite debugging parameter. */ virtual void OnRunTestSuite(); +#endif /** Called for every item in a NAMES list, so that modules may reformat portions of it as they see fit. - * For example NAMESX, channel mode +u and +I, and UHNAMES. If the nick is set to an empty string by any - * module, then this will cause the nickname not to be displayed at all. + * For example NAMESX, channel mode +u and +I, and UHNAMES. + * @param issuer The user who is going to receive the NAMES list being built + * @param item The channel member being considered for inclusion + * @param prefixes The prefix character(s) to display, initially set to the prefix char of the most powerful + * prefix mode the member has, can be changed + * @param nick The nick to display, initially set to the member's nick, can be changed + * @return Return MOD_RES_PASSTHRU to allow the member to be displayed, MOD_RES_DENY to cause them to be + * excluded from this NAMES list */ - virtual void OnNamesListItem(User* issuer, Membership* item, std::string &prefixes, std::string &nick); + virtual ModResult OnNamesListItem(User* issuer, Membership* item, std::string& prefixes, std::string& nick); - virtual ModResult OnNumeric(User* user, unsigned int numeric, const std::string &text); + virtual ModResult OnNumeric(User* user, const Numeric::Numeric& numeric); /** Called whenever a result from /WHO is about to be returned * @param source The user running the /WHO query * @param params The parameters to the /WHO query * @param user The user that this line of the query is about - * @param line The raw line to send; modifiable, if empty no line will be returned. + * @param memb The member shown in this line, NULL if no channel is in this line + * @param numeric Numeric to send; modifiable. + * @return MOD_RES_PASSTHRU to allow the line to be displayed, MOD_RES_DENY to hide it */ - virtual void OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, std::string& line); + virtual ModResult OnSendWhoLine(User* source, const std::vector<std::string>& params, User* user, Membership* memb, Numeric::Numeric& numeric); /** Called whenever a local user's IP is set for the first time, or when a local user's IP changes due to * a module like m_cgiirc changing it. * @param user The user whose IP is being set */ virtual void OnSetUserIP(LocalUser* user); -}; - - -#define CONF_NO_ERROR 0x000000 -#define CONF_NOT_A_NUMBER 0x000010 -#define CONF_INT_NEGATIVE 0x000080 -#define CONF_VALUE_NOT_FOUND 0x000100 -#define CONF_FILE_NOT_FOUND 0x000200 - - -/** Allows reading of values from configuration files - * This class allows a module to read from either the main configuration file (inspircd.conf) or from - * a module-specified configuration file. It may either be instantiated with one parameter or none. - * Constructing the class using one parameter allows you to specify a path to your own configuration - * file, otherwise, inspircd.conf is read. - */ -class CoreExport ConfigReader : public interfacebase -{ - protected: - /** Error code - */ - long error; - - public: - /** Default constructor. - * This constructor initialises the ConfigReader class to read the inspircd.conf file - * as specified when running ./configure. - */ - ConfigReader(); - /** Default destructor. - * This method destroys the ConfigReader class. - */ - ~ConfigReader(); - - /** Retrieves a value from the config file. - * This method retrieves a value from the config file. Where multiple copies of the tag - * exist in the config file, index indicates which of the values to retrieve. - */ - std::string ReadValue(const std::string &tag, const std::string &name, int index, bool allow_linefeeds = false); - /** Retrieves a value from the config file. - * This method retrieves a value from the config file. Where multiple copies of the tag - * exist in the config file, index indicates which of the values to retrieve. If the - * tag is not found the default value is returned instead. - */ - std::string ReadValue(const std::string &tag, const std::string &name, const std::string &default_value, int index, bool allow_linefeeds = false); - - /** Retrieves a boolean value from the config file. - * This method retrieves a boolean value from the config file. Where multiple copies of the tag - * exist in the config file, index indicates which of the values to retrieve. The values "1", "yes" - * and "true" in the config file count as true to ReadFlag, and any other value counts as false. - */ - bool ReadFlag(const std::string &tag, const std::string &name, int index); - /** Retrieves a boolean value from the config file. - * This method retrieves a boolean value from the config file. Where multiple copies of the tag - * exist in the config file, index indicates which of the values to retrieve. The values "1", "yes" - * and "true" in the config file count as true to ReadFlag, and any other value counts as false. - * If the tag is not found, the default value is used instead. - */ - bool ReadFlag(const std::string &tag, const std::string &name, const std::string &default_value, int index); - - /** Retrieves an integer value from the config file. - * This method retrieves an integer value from the config file. Where multiple copies of the tag - * exist in the config file, index indicates which of the values to retrieve. Any invalid integer - * values in the tag will cause the objects error value to be set, and any call to GetError() will - * return CONF_INVALID_NUMBER to be returned. need_positive is set if the number must be non-negative. - * If a negative number is placed into a tag which is specified positive, 0 will be returned and GetError() - * will return CONF_INT_NEGATIVE. Note that need_positive is not suitable to get an unsigned int - you - * should cast the result to achieve that effect. - */ - int ReadInteger(const std::string &tag, const std::string &name, int index, bool need_positive); - /** Retrieves an integer value from the config file. - * This method retrieves an integer value from the config file. Where multiple copies of the tag - * exist in the config file, index indicates which of the values to retrieve. Any invalid integer - * values in the tag will cause the objects error value to be set, and any call to GetError() will - * return CONF_INVALID_NUMBER to be returned. needs_unsigned is set if the number must be unsigned. - * If a signed number is placed into a tag which is specified unsigned, 0 will be returned and GetError() - * will return CONF_NOT_UNSIGNED. If the tag is not found, the default value is used instead. - */ - int ReadInteger(const std::string &tag, const std::string &name, const std::string &default_value, int index, bool need_positive); - - /** Returns the last error to occur. - * Valid errors can be found by looking in modules.h. Any nonzero value indicates an error condition. - * A call to GetError() resets the error flag back to 0. - */ - long GetError(); - - /** Counts the number of times a given tag appears in the config file. - * This method counts the number of times a tag appears in a config file, for use where - * there are several tags of the same kind, e.g. with opers and connect types. It can be - * used with the index value of ConfigReader::ReadValue to loop through all copies of a - * multiple instance tag. - */ - int Enumerate(const std::string &tag); -}; - - - -/** Caches a text file into memory and can be used to retrieve lines from it. - * This class contains methods for read-only manipulation of a text file in memory. - * Either use the constructor type with one parameter to load a file into memory - * at construction, or use the LoadFile method to load a file. - */ -class CoreExport FileReader : public classbase -{ - /** The file contents - */ - std::vector<std::string> fc; - /** Content size in bytes + /** Called whenever a ServiceProvider is registered. + * @param service ServiceProvider being registered. */ - unsigned long contentsize; + virtual void OnServiceAdd(ServiceProvider& service); - /** Calculate content size in bytes + /** Called whenever a ServiceProvider is unregistered. + * @param service ServiceProvider being unregistered. */ - void CalcSize(); + virtual void OnServiceDel(ServiceProvider& service); - public: - /** Default constructor. - * This method does not load any file into memory, you must use the LoadFile method - * after constructing the class this way. - */ - FileReader(); - - /** Secondary constructor. - * This method initialises the class with a file loaded into it ready for GetLine and - * and other methods to be called. If the file could not be loaded, FileReader::FileSize - * returns 0. - */ - FileReader(const std::string &filename); - - /** Default destructor. - * This deletes the memory allocated to the file. - */ - ~FileReader(); - - /** Used to load a file. - * This method loads a file into the class ready for GetLine and - * and other methods to be called. If the file could not be loaded, FileReader::FileSize - * returns 0. - */ - void LoadFile(const std::string &filename); - - /** Returns the whole content of the file as std::string - */ - std::string Contents(); - - /** Returns the entire size of the file as std::string - */ - unsigned long ContentSize(); - - /** Returns true if the file exists - * This function will return false if the file could not be opened. - */ - bool Exists(); - - /** Retrieve one line from the file. - * This method retrieves one line from the text file. If an empty non-NULL string is returned, - * the index was out of bounds, or the line had no data on it. - */ - std::string GetLine(int x); - - /** Returns the size of the file in lines. - * This method returns the number of lines in the read file. If it is 0, no lines have been - * read into memory, either because the file is empty or it does not exist, or cannot be - * opened due to permission problems. - */ - int FileSize(); + virtual ModResult OnUserWrite(LocalUser* user, ClientProtocol::Message& msg); }; -/** A list of modules - */ -typedef std::vector<Module*> IntModuleList; - -/** An event handler iterator - */ -typedef IntModuleList::iterator EventHandlerIter; - /** ModuleManager takes care of all things module-related * in the core. */ -class CoreExport ModuleManager +class CoreExport ModuleManager : public fakederef<ModuleManager> { + public: + typedef std::vector<ServiceProvider*> ServiceList; + private: /** Holds a string describing the last module error to occur */ std::string LastModuleError; - /** Total number of modules loaded into the ircd - */ - int ModCount; - /** List of loaded modules and shared object/dll handles * keyed by module name */ @@ -1506,18 +969,49 @@ class CoreExport ModuleManager PRIO_STATE_LAST } prioritizationState; - /** Internal unload module hook */ - bool CanUnload(Module*); + /** Loads all core modules (core_*) + */ + void LoadCoreModules(std::map<std::string, ServiceList>& servicemap); + + /** Calls the Prioritize() method in all loaded modules + * @return True if all went well, false if a dependency loop was detected + */ + bool PrioritizeHooks(); + + /** Unregister all user modes or all channel modes owned by a module + * @param mod Module whose modes to unregister + * @param modetype MODETYPE_USER to unregister user modes, MODETYPE_CHANNEL to unregister channel modes + */ + void UnregisterModes(Module* mod, ModeType modetype); + public: + typedef std::map<std::string, Module*> ModuleMap; /** Event handler hooks. * This needs to be public to be used by FOREACH_MOD and friends. */ - IntModuleList EventHandlers[I_END]; + Module::List EventHandlers[I_END]; /** List of data services keyed by name */ std::multimap<std::string, ServiceProvider*> DataProviders; + /** A list of ServiceProviders waiting to be registered. + * Non-NULL when constructing a Module, NULL otherwise. + * When non-NULL ServiceProviders add themselves to this list on creation and the core + * automatically registers them (that is, call AddService()) after the Module is constructed, + * and before Module::init() is called. + * If a service is created after the construction of the Module (for example in init()) it + * has to be registered manually. + */ + ServiceList* NewServices; + + /** Expands the name of a module by prepending "m_" and appending ".so". + * No-op if the name already has the ".so" extension. + * @param modname Module name to expand + * @return Module name starting with "m_" and ending with ".so" + */ + static std::string ExpandModName(const std::string& modname); + /** Simple, bog-standard, boring constructor. */ ModuleManager(); @@ -1544,12 +1038,6 @@ class CoreExport ModuleManager */ bool SetPriority(Module* mod, Implementation i, Priority s, Module* which = NULL); - /** Backwards compat interface */ - inline bool SetPriority(Module* mod, Implementation i, Priority s, Module** dptr) - { - return SetPriority(mod, i, s, *dptr); - } - /** Change the priority of all events in a module. * @param mod The module to set the priority of * @param s The priority of all events in the module. @@ -1558,7 +1046,7 @@ class CoreExport ModuleManager * SetPriority method for this, where you may specify other modules to * be prioritized against. */ - bool SetPriority(Module* mod, Priority s); + void SetPriority(Module* mod, Priority s); /** Attach an event to a module. * You may later detatch the event with ModuleManager::Detach(). @@ -1590,6 +1078,11 @@ class CoreExport ModuleManager */ void DetachAll(Module* mod); + /** Attach all events to a module (used on module load) + * @param mod Module to attach to all events + */ + void AttachAll(Module* mod); + /** Returns text describing the last module error * @return The last error message to occur */ @@ -1609,25 +1102,18 @@ class CoreExport ModuleManager */ bool Unload(Module* module); - /** Run an asynchronous reload of the given module. When the reload is - * complete, the callback will be run with true if the reload succeeded - * and false if it did not. - */ - void Reload(Module* module, HandlerBase1<void, bool>* callback); - /** Called by the InspIRCd constructor to load all modules from the config file. */ void LoadAll(); void UnloadAll(); void DoSafeUnload(Module*); - /** Get the total number of currently loaded modules - * @return The number of loaded modules + /** Check if a module can be unloaded and if yes, prepare it for unload + * @param mod Module to be unloaded + * @return True if the module is unloadable, false otherwise. + * If true the module must be unloaded in the current main loop iteration. */ - int GetCount() - { - return this->ModCount; - } + bool CanUnload(Module* mod); /** Find a module by name, and return a Module* to it. * This is preferred over iterating the module lists yourself. @@ -1642,6 +1128,11 @@ class CoreExport ModuleManager /** Unregister a service provided by a module */ void DelService(ServiceProvider&); + /** Register all services in a given ServiceList + * @param list The list containing the services to register + */ + void AddServices(const ServiceList& list); + inline void AddServices(ServiceProvider** list, int count) { for(int i=0; i < count; i++) @@ -1658,13 +1149,21 @@ class CoreExport ModuleManager return static_cast<T*>(FindService(SERVICE_DATA, name)); } - /** Return a list of all modules matching the given filter - * @param filter This int is a bitmask of flags set in Module::Flags, - * such as VF_VENDOR or VF_STATIC. If you wish to receive a list of - * all modules with no filtering, set this to 0. - * @return The list of module names + /** Get a map of all loaded modules keyed by their name + * @return A ModuleMap containing all loaded modules + */ + const ModuleMap& GetModules() const { return Modules; } + + /** Make a service referenceable by dynamic_references + * @param name Name that will be used by dynamic_references to find the object + * @param service Service to make referenceable by dynamic_references + */ + void AddReferent(const std::string& name, ServiceProvider* service); + + /** Make a service no longer referenceable by dynamic_references + * @param service Service to make no longer referenceable by dynamic_references */ - const std::vector<std::string> GetAllModuleNames(int filter); + void DelReferent(ServiceProvider* service); }; /** Do not mess with these functions unless you know the C preprocessor @@ -1677,66 +1176,13 @@ class CoreExport ModuleManager #define MODULE_INIT_SYM_FN_2(x,y) MODULE_INIT_SYM_FN_1(x,y) #define MODULE_INIT_SYM_FN_1(x,y) inspircd_module_ ## x ## _ ## y -#ifdef PURE_STATIC - -struct AllCommandList { - typedef Command* (*fn)(Module*); - AllCommandList(fn cmd); -}; -#define COMMAND_INIT(x) static Command* MK_ ## x(Module* m) { return new x(m); } \ - static const AllCommandList PREP_ ## x(&MK_ ## x); - -struct AllModuleList { - typedef Module* (*fn)(); - fn init; - std::string name; - AllModuleList(fn mod, const std::string& Name); -}; - -#define MODULE_INIT(x) static Module* MK_ ## x() { return new x; } \ - static const AllModuleList PREP_ ## x(&MK_ ## x, MODNAMESTR); - -#define MODNAMESTR MODNAMESTR_FN_2(MODNAME) -#define MODNAMESTR_FN_2(x) MODNAMESTR_FN_1(x) -#define MODNAMESTR_FN_1(x) #x - -#else - /** This definition is used as shorthand for the various classes * and functions needed to make a module loadable by the OS. * It defines the class factory and external init_module function. */ -#ifdef _WIN32 - -#define MODULE_INIT(y) \ - extern "C" DllExport Module * MODULE_INIT_SYM() \ - { \ - return new y; \ - } \ - BOOLEAN WINAPI DllMain(HINSTANCE hDllHandle, DWORD nReason, LPVOID Reserved) \ - { \ - switch ( nReason ) \ - { \ - case DLL_PROCESS_ATTACH: \ - case DLL_PROCESS_DETACH: \ - break; \ - } \ - return TRUE; \ - } \ - extern "C" DllExport const char inspircd_src_version[] = VERSION " r" REVISION; - -#else - #define MODULE_INIT(y) \ extern "C" DllExport Module * MODULE_INIT_SYM() \ { \ return new y; \ } \ - extern "C" const char inspircd_src_version[] = VERSION " r" REVISION; -#endif - -#define COMMAND_INIT(c) MODULE_INIT(CommandModule<c>) - -#endif - -#endif + extern "C" DllExport const char inspircd_src_version[] = INSPIRCD_VERSION; diff --git a/include/modules/account.h b/include/modules/account.h new file mode 100644 index 000000000..0368127a6 --- /dev/null +++ b/include/modules/account.h @@ -0,0 +1,48 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc> + * + * 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/>. + */ + + +#pragma once + +#include <map> +#include <string> + +#include "event.h" + +typedef StringExtItem AccountExtItem; + +inline AccountExtItem* GetAccountExtItem() +{ + return static_cast<AccountExtItem*>(ServerInstance->Extensions.GetItem("accountname")); +} + +class AccountEventListener : public Events::ModuleEventListener +{ + public: + AccountEventListener(Module* mod) + : ModuleEventListener(mod, "event/account") + { + } + + /** Called when a user logs in or logs out + * @param user User logging in or out + * @param newaccount New account name of the user or empty string if the user + * logged out + */ + virtual void OnAccountChange(User* user, const std::string& newaccount) = 0; +}; diff --git a/include/modules/away.h b/include/modules/away.h new file mode 100644 index 000000000..f231f0a06 --- /dev/null +++ b/include/modules/away.h @@ -0,0 +1,83 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2016-2017 Peter Powell <petpow@saberuk.com> + * + * 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/>. + */ + + +#pragma once + +#include "event.h" + +namespace Away +{ + class EventListener; + class EventProvider; +} + +class Away::EventListener + : public Events::ModuleEventListener +{ + protected: + EventListener(Module* mod) + : ModuleEventListener(mod, "event/away") + { + } + + public: + /** Called when a user wishes to mark themselves as away. + * @param user The user who is going away. + * @param message The away message that the user set. + * @return Either MOD_RES_ALLOW to allow the user to mark themself as away, MOD_RES_DENY to + * disallow the user to mark themself as away, or MOD_RES_PASSTHRU to let another module + * handle the event. + */ + virtual ModResult OnUserPreAway(LocalUser* user, std::string& message) + { + return MOD_RES_PASSTHRU; + } + + /** Called when a user wishes to mark themselves as back. + * @param user The user who is going away. + * @param message The away message that the user set. + * @return Either MOD_RES_ALLOW to allow the user to mark themself as back, MOD_RES_DENY to + * disallow the user to mark themself as back, or MOD_RES_PASSTHRU to let another module + * handle the event. + */ + virtual ModResult OnUserPreBack(LocalUser* user) + { + return MOD_RES_PASSTHRU; + } + + /** Called when a user has marked themself as away. + * @param user The user who has gone away. + */ + virtual void OnUserAway(User* user) = 0; + + /** Called when a user has returned from being away. + * @param user The user who has returned from being away. + */ + virtual void OnUserBack(User* user) = 0; +}; + +class Away::EventProvider + : public Events::ModuleEventProvider +{ + public: + EventProvider(Module* mod) + : ModuleEventProvider(mod, "event/away") + { + } +}; diff --git a/include/modules/cap.h b/include/modules/cap.h new file mode 100644 index 000000000..6dcb9f3bc --- /dev/null +++ b/include/modules/cap.h @@ -0,0 +1,335 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com> + * + * 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/>. + */ + + +#pragma once + +#include "event.h" + +namespace Cap +{ + static const unsigned int MAX_CAPS = (sizeof(intptr_t) * 8) - 1; + static const intptr_t CAP_302_BIT = (intptr_t)1 << MAX_CAPS; + static const unsigned int MAX_VALUE_LENGTH = 100; + + typedef intptr_t Ext; + class ExtItem : public LocalIntExt + { + public: + ExtItem(Module* mod); + std::string serialize(SerializeFormat format, const Extensible* container, void* item) const CXX11_OVERRIDE; + void unserialize(SerializeFormat format, Extensible* container, const std::string& value) CXX11_OVERRIDE; + }; + + class Capability; + + enum Protocol + { + /** Supports capability negotiation protocol v3.1, or none + */ + CAP_LEGACY, + + /** Supports capability negotiation v3.2 + */ + CAP_302 + }; + + class EventListener : public Events::ModuleEventListener + { + public: + EventListener(Module* mod) + : ModuleEventListener(mod, "event/cap") + { + } + + /** Called whenever a new client capability becomes available or unavailable + * @param cap Capability being added or removed + * @param add If true, the capability is being added, otherwise its being removed + */ + virtual void OnCapAddDel(Capability* cap, bool add) = 0; + + /** Called whenever the value of a cap changes. + * @param cap Capability whose value changed + */ + virtual void OnCapValueChange(Capability* cap) { } + }; + + class Manager : public DataProvider + { + public: + Manager(Module* mod) + : DataProvider(mod, "capmanager") + { + } + + /** Register a client capability. + * Modules should call Capability::SetActive(true) instead of this method. + * @param cap Capability to register + */ + virtual void AddCap(Capability* cap) = 0; + + /** Unregister a client capability. + * Modules should call Capability::SetActive(false) instead of this method. + * @param cap Capability to unregister + */ + virtual void DelCap(Capability* cap) = 0; + + /** Find a capability by name + * @param name Capability to find + * @return Capability object pointer if found, NULL otherwise + */ + virtual Capability* Find(const std::string& name) const = 0; + + /** Notify manager when a value of a cap changed + * @param cap Cap whose value changed + */ + virtual void NotifyValueChange(Capability* cap) = 0; + }; + + /** Represents a client capability. + * + * Capabilities offer extensions to the client to server protocol. They must be negotiated with clients before they have any effect on the protocol. + * Each cap must have a unique name that is used during capability negotiation. + * + * After construction the cap is ready to be used by clients without any further setup, like other InspIRCd services. + * The get() method accepts a user as parameter and can be used to check whether that user has negotiated usage of the cap. This is only known for local users. + * + * The cap module must be loaded for the capability to work. The IsRegistered() method can be used to query whether the cap is actually online or not. + * The capability can be deactivated and reactivated with the SetActive() method. Deactivated caps behave as if they don't exist. + * + * It is possible to implement special behavior by inheriting from this class and overriding some of its methods. + */ + class Capability : public ServiceProvider, private dynamic_reference_base::CaptureHook + { + typedef size_t Bit; + + /** Bit allocated to this cap, undefined if the cap is unregistered + */ + Bit bit; + + /** Extension containing all caps set by a user. NULL if the cap is unregistered. + */ + ExtItem* extitem; + + /** True if the cap is active. Only active caps are registered in the manager. + */ + bool active; + + /** Reference to the cap manager object + */ + dynamic_reference<Manager> manager; + + void OnCapture() CXX11_OVERRIDE + { + if (active) + SetActive(true); + } + + void Unregister() + { + bit = 0; + extitem = NULL; + } + + Ext AddToMask(Ext mask) const { return (mask | GetMask()); } + Ext DelFromMask(Ext mask) const { return (mask & (~GetMask())); } + Bit GetMask() const { return bit; } + + friend class ManagerImpl; + + protected: + /** Notify the manager that the value of the capability changed. + * Must be called if the value of the cap changes for any reason. + */ + void NotifyValueChange() + { + if (IsRegistered()) + manager->NotifyValueChange(this); + } + + public: + /** Constructor, initializes the capability. + * Caps are active by default. + * @param mod Module providing the cap + * @param Name Raw name of the cap as used in the protocol (CAP LS, etc.) + */ + Capability(Module* mod, const std::string& Name) + : ServiceProvider(mod, Name, SERVICE_CUSTOM) + , active(true) + , manager(mod, "capmanager") + { + Unregister(); + } + + ~Capability() + { + SetActive(false); + } + + void RegisterService() CXX11_OVERRIDE + { + manager.SetCaptureHook(this); + SetActive(true); + } + + /** Check whether a user has the capability turned on. + * This method is safe to call if the cap is unregistered and will return false. + * @param user User to check + * @return True if the user is using this capability, false otherwise + */ + bool get(User* user) const + { + if (!IsRegistered()) + return false; + Ext caps = extitem->get(user); + return ((caps & GetMask()) != 0); + } + + /** Turn the capability on/off for a user. If the cap is not registered this method has no effect. + * @param user User to turn the cap on/off for + * @param val True to turn the cap on, false to turn it off + */ + void set(User* user, bool val) + { + if (!IsRegistered()) + return; + Ext curr = extitem->get(user); + extitem->set(user, (val ? AddToMask(curr) : DelFromMask(curr))); + } + + /** Activate or deactivate the capability. + * If activating, the cap is marked as active and if the manager is available the cap is registered in the manager. + * If deactivating, the cap is marked as inactive and if it is registered, it will be unregistered. + * Users who had the cap turned on will have it turned off automatically. + * @param activate True to activate the cap, false to deactivate it + */ + void SetActive(bool activate) + { + active = activate; + if (manager) + { + if (activate) + manager->AddCap(this); + else + manager->DelCap(this); + } + } + + /** Get the name of the capability that's used in the protocol + * @return Name of the capability as used in the protocol + */ + const std::string& GetName() const { return name; } + + /** Check whether the capability is active. The cap must be active and registered to be used by users. + * @return True if the cap is active, false if it has been deactivated + */ + bool IsActive() const { return active; } + + /** Check whether the capability is registered + * The cap must be active and the manager must be available for a cap to be registered. + * @return True if the cap is registered in the manager, false otherwise + */ + bool IsRegistered() const { return (extitem != NULL); } + + /** Get the CAP negotiation protocol version of a user. + * The cap must be registered for this to return anything other than CAP_LEGACY. + * @param user User whose negotiation protocol version to query + * @return One of the Capability::Protocol enum indicating the highest supported capability negotiation protocol version + */ + Protocol GetProtocol(LocalUser* user) const + { + return ((IsRegistered() && (extitem->get(user) & CAP_302_BIT)) ? CAP_302 : CAP_LEGACY); + } + + /** Called when a user requests to turn this capability on or off. + * @param user User requesting to change the state of the cap + * @param add True if requesting to turn the cap on, false if requesting to turn it off + * @return True to allow the request, false to reject it + */ + virtual bool OnRequest(LocalUser* user, bool add) + { + return true; + } + + /** Called when a user requests a list of all capabilities and this capability is about to be included in the list. + * The default behavior always includes the cap in the list. + * @param user User querying a list capabilities + * @return True to add this cap to the list sent to the user, false to not list it + */ + virtual bool OnList(LocalUser* user) + { + return true; + } + + /** Query the value of this capability for a user + * @param user User who will get the value of the capability + * @return Value to show to the user. If NULL, the capability has no value (default). + */ + virtual const std::string* GetValue(LocalUser* user) const + { + return NULL; + } + }; + + /** Reference to a cap. The cap may be provided by another module. + */ + class Reference + { + dynamic_reference_nocheck<Capability> ref; + + public: + /** Constructor, initializes the capability reference + * @param mod Module creating this object + * @param Name Raw name of the cap as used in the protocol (CAP LS, etc.) + */ + Reference(Module* mod, const std::string& Name) + : ref(mod, "cap/" + Name) + { + } + + /** Check whether a user has the referenced capability turned on. + * @param user User to check + * @return True if the user is using the referenced capability, false otherwise + */ + bool get(LocalUser* user) + { + if (ref) + return ref->get(user); + return false; + } + }; + + class MessageBase : public ClientProtocol::Message + { + public: + MessageBase(const std::string& subcmd) + : ClientProtocol::Message("CAP", ServerInstance->Config->ServerName) + { + PushParamPlaceholder(); + PushParam(subcmd); + } + + void SetUser(LocalUser* user) + { + if (user->registered & REG_NICK) + ReplaceParamRef(0, user->nick); + else + ReplaceParam(0, "*"); + } + }; +} diff --git a/include/modules/dns.h b/include/modules/dns.h new file mode 100644 index 000000000..3db651798 --- /dev/null +++ b/include/modules/dns.h @@ -0,0 +1,204 @@ +/* + * 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/>. + */ + +#pragma once + +namespace DNS +{ + /** Valid query types + */ + enum QueryType + { + /* Nothing */ + QUERY_NONE, + /* A simple A lookup */ + QUERY_A = 1, + /* A CNAME lookup */ + QUERY_CNAME = 5, + /* Reverse DNS lookup */ + QUERY_PTR = 12, + /* TXT */ + QUERY_TXT = 16, + /* IPv6 AAAA lookup */ + QUERY_AAAA = 28 + }; + + /** Flags that can be AND'd into DNSPacket::flags to receive certain values + */ + enum + { + QUERYFLAGS_QR = 0x8000, + QUERYFLAGS_OPCODE = 0x7800, + QUERYFLAGS_AA = 0x400, + QUERYFLAGS_TC = 0x200, + QUERYFLAGS_RD = 0x100, + QUERYFLAGS_RA = 0x80, + QUERYFLAGS_Z = 0x70, + QUERYFLAGS_RCODE = 0xF + }; + + enum Error + { + ERROR_NONE, + ERROR_UNKNOWN, + ERROR_UNLOADED, + ERROR_TIMEDOUT, + ERROR_MALFORMED, + ERROR_NOT_AN_ANSWER, + ERROR_NONSTANDARD_QUERY, + ERROR_FORMAT_ERROR, + ERROR_SERVER_FAILURE, + ERROR_DOMAIN_NOT_FOUND, + ERROR_NOT_IMPLEMENTED, + ERROR_REFUSED, + ERROR_NO_RECORDS, + ERROR_INVALIDTYPE + }; + + typedef uint16_t RequestId; + + const int PORT = 53; + + class Exception : public ModuleException + { + public: + Exception(const std::string& message) : ModuleException(message) { } + }; + + struct Question + { + std::string name; + QueryType type; + + Question() : type(QUERY_NONE) { } + Question(const std::string& n, QueryType t) : name(n), type(t) { } + bool operator==(const Question& other) const { return ((name == other.name) && (type == other.type)); } + bool operator!=(const Question& other) const { return (!(*this == other)); } + + struct hash + { + size_t operator()(const Question& question) const + { + return irc::insensitive()(question.name); + } + }; + }; + + struct ResourceRecord : Question + { + unsigned int ttl; + std::string rdata; + time_t created; + + ResourceRecord(const std::string& n, QueryType t) : Question(n, t), ttl(0), created(ServerInstance->Time()) { } + ResourceRecord(const Question& question) : Question(question), ttl(0), created(ServerInstance->Time()) { } + }; + + struct Query + { + Question question; + std::vector<ResourceRecord> answers; + Error error; + bool cached; + + Query() : error(ERROR_NONE), cached(false) { } + Query(const Question& q) : question(q), error(ERROR_NONE), cached(false) { } + + const ResourceRecord* FindAnswerOfType(QueryType qtype) const + { + for (std::vector<DNS::ResourceRecord>::const_iterator i = answers.begin(); i != answers.end(); ++i) + { + const DNS::ResourceRecord& rr = *i; + if (rr.type == qtype) + return &rr; + } + + return NULL; + } + }; + + class ReplySocket; + class Request; + + /** DNS manager + */ + class Manager : public DataProvider + { + public: + Manager(Module* mod) : DataProvider(mod, "DNS") { } + + virtual void Process(Request* req) = 0; + virtual void RemoveRequest(Request* req) = 0; + virtual std::string GetErrorStr(Error) = 0; + }; + + /** A DNS query. + */ + class Request : public Timer + { + protected: + Manager* const manager; + public: + Question question; + /* Use result cache if available */ + bool use_cache; + /* Request id */ + RequestId id; + /* Creator of this request */ + Module* const creator; + + Request(Manager* mgr, Module* mod, const std::string& addr, QueryType qt, bool usecache = true) + : Timer(ServerInstance->Config->ConfValue("dns")->getDuration("timeout", 5, 1)) + , manager(mgr) + , question(addr, qt) + , use_cache(usecache) + , id(0) + , creator(mod) + { + } + + virtual ~Request() + { + manager->RemoveRequest(this); + } + + /** Called when this request succeeds + * @param r The query sent back from the nameserver + */ + virtual void OnLookupComplete(const Query* req) = 0; + + /** Called when this request fails or times out. + * @param r The query sent back from the nameserver, check the error code. + */ + virtual void OnError(const Query* req) { } + + /** Used to time out the query, calls OnError and asks the TimerManager + * to delete this request + */ + bool Tick(time_t now) CXX11_OVERRIDE + { + Query rr(this->question); + rr.error = ERROR_TIMEDOUT; + this->OnError(&rr); + delete this; + return false; + } + }; + +} // namespace DNS diff --git a/include/modules/exemption.h b/include/modules/exemption.h new file mode 100644 index 000000000..b590a5797 --- /dev/null +++ b/include/modules/exemption.h @@ -0,0 +1,75 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2016-2017 Peter Powell <petpow@saberuk.com> + * + * 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/>. + */ + + +#pragma once + +#include "event.h" + +namespace CheckExemption +{ + class EventListener; + class EventProvider; + + /** Helper function for calling the CheckExemption::EventListener::OnCheckExemption event. + * @param prov The CheckExemption::EventProvider which is calling the event. + * @param user The user to check exemption for. + * @param chan The channel to check exemption on. + * @param restriction The restriction to check for. + * @return Either MOD_RES_ALLOW if the exemption was confirmed, MOD_RES_DENY if the exemption was + * denied or MOD_RES_PASSTHRU if no module handled the event. + */ + inline ModResult Call(const CheckExemption::EventProvider& prov, User* user, Channel* chan, const std::string& restriction); +} + +class CheckExemption::EventListener + : public Events::ModuleEventListener +{ + protected: + EventListener(Module* mod) + : ModuleEventListener(mod, "event/exemption") + { + } + + public: + /** Called when checking if a user is exempt from something. + * @param user The user to check exemption for. + * @param chan The channel to check exemption on. + * @param restriction The restriction to check for. + * @return Either MOD_RES_ALLOW to confirm an exemption, MOD_RES_DENY to deny an exemption, + * or MOD_RES_PASSTHRU to let another module handle the event. + */ + virtual ModResult OnCheckExemption(User* user, Channel* chan, const std::string& restriction) = 0; +}; + +class CheckExemption::EventProvider + : public Events::ModuleEventProvider +{ + public: + EventProvider(Module* mod) + : ModuleEventProvider(mod, "event/exemption") + { + } +}; + +inline ModResult CheckExemption::Call(const CheckExemption::EventProvider& prov, User* user, Channel* chan, const std::string& restriction) +{ + ModResult result; + FIRST_MOD_RESULT_CUSTOM(prov, CheckExemption::EventListener, OnCheckExemption, result, (user, chan, restriction)); + return result; +} diff --git a/include/modules/hash.h b/include/modules/hash.h new file mode 100644 index 000000000..7d46ee74a --- /dev/null +++ b/include/modules/hash.h @@ -0,0 +1,72 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2010 Daniel De Graaf <danieldg@inspircd.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/>. + */ + + +#pragma once + +#include "modules.h" + +class HashProvider : public DataProvider +{ + public: + const unsigned int out_size; + const unsigned int block_size; + HashProvider(Module* mod, const std::string& Name, unsigned int osiz = 0, unsigned int bsiz = 0) + : DataProvider(mod, "hash/" + Name), out_size(osiz), block_size(bsiz) + { + } + + virtual std::string GenerateRaw(const std::string& data) = 0; + + virtual std::string ToPrintable(const std::string& raw) + { + return BinToHex(raw); + } + + virtual bool Compare(const std::string& input, const std::string& hash) + { + return InspIRCd::TimingSafeCompare(Generate(input), hash); + } + + std::string Generate(const std::string& data) + { + return ToPrintable(GenerateRaw(data)); + } + + /** HMAC algorithm, RFC 2104 */ + std::string hmac(const std::string& key, const std::string& msg) + { + std::string hmac1, hmac2; + std::string kbuf = key.length() > block_size ? GenerateRaw(key) : key; + kbuf.resize(block_size); + + for (size_t n = 0; n < block_size; n++) + { + hmac1.push_back(static_cast<char>(kbuf[n] ^ 0x5C)); + hmac2.push_back(static_cast<char>(kbuf[n] ^ 0x36)); + } + hmac2.append(msg); + hmac1.append(GenerateRaw(hmac2)); + return GenerateRaw(hmac1); + } + + bool IsKDF() const + { + return (!block_size); + } +}; diff --git a/include/modules/httpd.h b/include/modules/httpd.h new file mode 100644 index 000000000..b4b88bed5 --- /dev/null +++ b/include/modules/httpd.h @@ -0,0 +1,262 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org> + * Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com> + * Copyright (C) 2007 John Brooks <john.brooks@dereferenced.net> + * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org> + * Copyright (C) 2006 Craig Edwards <craigedwards@brainbox.cc> + * + * 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/>. + */ + + +#pragma once + +#include "base.h" +#include "event.h" + +#include <string> +#include <sstream> +#include <map> + +/** A modifyable list of HTTP header fields + */ +class HTTPHeaders +{ + protected: + std::map<std::string,std::string> headers; + public: + + /** Set the value of a header + * Sets the value of the named header. If the header is already present, it will be replaced + */ + void SetHeader(const std::string &name, const std::string &data) + { + headers[name] = data; + } + + /** Set the value of a header, only if it doesn't exist already + * Sets the value of the named header. If the header is already present, it will NOT be updated + */ + void CreateHeader(const std::string &name, const std::string &data) + { + if (!IsSet(name)) + SetHeader(name, data); + } + + /** Remove the named header + */ + void RemoveHeader(const std::string &name) + { + headers.erase(name); + } + + /** Remove all headers + */ + void Clear() + { + headers.clear(); + } + + /** Get the value of a header + * @return The value of the header, or an empty string + */ + std::string GetHeader(const std::string &name) + { + std::map<std::string,std::string>::iterator it = headers.find(name); + if (it == headers.end()) + return std::string(); + + return it->second; + } + + /** Check if the given header is specified + * @return true if the header is specified + */ + bool IsSet(const std::string &name) + { + std::map<std::string,std::string>::iterator it = headers.find(name); + return (it != headers.end()); + } + + /** Get all headers, formatted by the HTTP protocol + * @return Returns all headers, formatted according to the HTTP protocol. There is no request terminator at the end + */ + std::string GetFormattedHeaders() + { + std::string re; + + for (std::map<std::string,std::string>::iterator i = headers.begin(); i != headers.end(); i++) + re += i->first + ": " + i->second + "\r\n"; + + return re; + } +}; + +class HttpServerSocket; + +/** This class represents a HTTP request. + */ +class HTTPRequest +{ + protected: + std::string type; + std::string document; + std::string ipaddr; + std::string postdata; + + public: + + HTTPHeaders *headers; + int errorcode; + + /** A socket pointer, which you must return in your HTTPDocument class + * if you reply to this request. + */ + HttpServerSocket* sock; + + /** Initialize HTTPRequest. + * This constructor is called by m_httpd.so to initialize the class. + * @param request_type The request type, e.g. GET, POST, HEAD + * @param uri The URI, e.g. /page + * @param hdr The headers sent with the request + * @param opaque An opaque pointer used internally by m_httpd, which you must pass back to the module in your reply. + * @param ip The IP address making the web request. + * @param pdata The post data (content after headers) received with the request, up to Content-Length in size + */ + HTTPRequest(const std::string& request_type, const std::string& uri, + HTTPHeaders* hdr, HttpServerSocket* socket, const std::string &ip, const std::string &pdata) + : type(request_type), document(uri), ipaddr(ip), postdata(pdata), headers(hdr), sock(socket) + { + } + + /** Get the post data (request content). + * All post data will be returned, including carriage returns and linefeeds. + * @return The postdata + */ + std::string& GetPostData() + { + return postdata; + } + + /** Get the request type. + * Any request type can be intercepted, even ones which are invalid in the HTTP/1.1 spec. + * @return The request type, e.g. GET, POST, HEAD + */ + std::string& GetType() + { + return type; + } + + /** Get URI. + * The URI string (URL minus hostname and scheme) will be provided by this function. + * @return The URI being requested + */ + std::string& GetURI() + { + return document; + } + + /** Get IP address of requester. + * The requesting system's ip address will be returned. + * @return The IP address as a string + */ + std::string& GetIP() + { + return ipaddr; + } +}; + +/** If you want to reply to HTTP requests, you must return a HTTPDocumentResponse to + * the httpd module via the HTTPdAPI. + * When you initialize this class you initialize it with all components required to + * form a valid HTTP response: the document data and a response code. + * You can add additional HTTP headers, if you want. + */ +class HTTPDocumentResponse +{ + public: + /** Module that generated this reply + */ + Module* const module; + + std::stringstream* document; + unsigned int responsecode; + + /** Any extra headers to include with the defaults + */ + HTTPHeaders headers; + + HTTPRequest& src; + + /** Initialize a HTTPDocumentResponse ready for sending to the httpd module. + * @param mod A pointer to the module who responded to the request + * @param req The request you obtained from the HTTPRequest at an earlier time + * @param doc A stringstream containing the document body + * @param response A valid HTTP/1.0 or HTTP/1.1 response code. The response text will be determined for you + * based upon the response code. + */ + HTTPDocumentResponse(Module* mod, HTTPRequest& req, std::stringstream* doc, unsigned int response) + : module(mod), document(doc), responsecode(response), src(req) + { + } +}; + +class HTTPdAPIBase : public DataProvider +{ + public: + HTTPdAPIBase(Module* parent) + : DataProvider(parent, "m_httpd_api") + { + } + + /** Answer an incoming HTTP request with the provided document + * @param response The response created by your module that will be sent to the client + */ + virtual void SendResponse(HTTPDocumentResponse& response) = 0; +}; + +/** The API provided by the httpd module that allows other modules to respond to incoming + * HTTP requests + */ +class HTTPdAPI : public dynamic_reference<HTTPdAPIBase> +{ + public: + HTTPdAPI(Module* parent) + : dynamic_reference<HTTPdAPIBase>(parent, "m_httpd_api") + { + } +}; + +class HTTPACLEventListener : public Events::ModuleEventListener +{ + public: + HTTPACLEventListener(Module* mod) + : ModuleEventListener(mod, "event/http-acl") + { + } + + virtual ModResult OnHTTPACLCheck(HTTPRequest& req) = 0; +}; + +class HTTPRequestEventListener : public Events::ModuleEventListener +{ + public: + HTTPRequestEventListener(Module* mod) + : ModuleEventListener(mod, "event/http-request") + { + } + + virtual ModResult OnHTTPRequest(HTTPRequest& req) = 0; +}; diff --git a/include/modules/invite.h b/include/modules/invite.h new file mode 100644 index 000000000..e53d5202f --- /dev/null +++ b/include/modules/invite.h @@ -0,0 +1,128 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2012, 2015 Attila Molnar <attilamolnar@hush.com> + * + * 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/>. + */ + + +#pragma once + +namespace Invite +{ + class APIBase; + class API; + class Invite; + + typedef insp::intrusive_list<Invite, LocalUser> List; +} + +class Invite::APIBase : public DataProvider +{ + public: + APIBase(Module* parent); + + /** Create or extend an Invite. + * When a user is invited to join a channel either a new Invite object is created or + * or the expiration timestamp is updated if there is already a pending Invite for + * the given (user, channel) pair and the new expiration time is further than the current. + * @param user Target user + * @param chan Target channel + * @param timeout Timestamp when the invite should expire, 0 for no expiration + */ + virtual void Create(LocalUser* user, Channel* chan, time_t timeout) = 0; + + /** Retrieves the Invite object for the given (user, channel) pair + * @param user Target user + * @param chan Target channel + * @return Invite object for the given (channel, user) pair if it exists, NULL otherwise + */ + virtual Invite* Find(LocalUser* user, Channel* chan) = 0; + + /** Returns the list of channels a user has been invited to but has not yet joined. + * @param user User whose invite list to retrieve + * @return List of channels the user is invited to or NULL if the list is empty + */ + virtual const List* GetList(LocalUser* user) = 0; + + /** Check if a user is invited to a channel + * @param user User to check + * @param chan Channel to check + * @return True if the user is invited to the channel, false otherwise + */ + bool IsInvited(LocalUser* user, Channel* chan) { return (Find(user, chan) != NULL); } + + /** Removes an Invite if it exists + * @param user User whose invite to remove + * @param chan Channel to remove the invite to + * @return True if the user was invited to the channel and the invite was removed, false if the user wasn't invited + */ + virtual bool Remove(LocalUser* user, Channel* chan) = 0; +}; + +class Invite::API : public dynamic_reference<APIBase> +{ + public: + API(Module* parent) + : dynamic_reference<APIBase>(parent, "core_channel_invite") + { + } +}; + +/** + * The Invite class contains all data about a pending invite. + * Invite objects are referenced from the user and the channel they belong to. + */ +class Invite::Invite : public insp::intrusive_list_node<Invite, LocalUser>, public insp::intrusive_list_node<Invite, Channel> +{ + public: + /** User the invite is for + */ + LocalUser* const user; + + /** Channel where the user is invited to + */ + Channel* const chan; + + /** Check whether the invite will expire or not + * @return True if the invite is timed, false if it doesn't expire + */ + bool IsTimed() const { return (expiretimer != NULL); } + + /** Serialize this object + * @param format Serialization format + * @param show_chans True to include channel in the output, false to include the nick/uuid + * @param out Output will be appended to this string + */ + void Serialize(SerializeFormat format, bool show_chans, std::string& out); + + friend class APIImpl; + + private: + /** Timer handling expiration. If NULL this invite doesn't expire. + */ + Timer* expiretimer; + + /** Constructor, only available to the module providing the invite API (core_channel). + * To create Invites use InviteAPI::Create(). + * @param user User being invited + * @param chan Channel the user is invited to + */ + Invite(LocalUser* user, Channel* chan); + + /** Destructor, only available to the module providing the invite API (core_channel). + * To remove Invites use InviteAPI::Remove(). + */ + ~Invite(); +}; diff --git a/include/modules/ircv3.h b/include/modules/ircv3.h new file mode 100644 index 000000000..338abdeba --- /dev/null +++ b/include/modules/ircv3.h @@ -0,0 +1,101 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com> + * + * 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/>. + */ + + +#pragma once + +#include "modules/cap.h" + +namespace IRCv3 +{ + class WriteNeighborsWithCap; + template <typename T> + class CapTag; +} + +class IRCv3::WriteNeighborsWithCap : public User::ForEachNeighborHandler +{ + const Cap::Capability& cap; + ClientProtocol::Event& protoev; + + void Execute(LocalUser* user) CXX11_OVERRIDE + { + if (cap.get(user)) + user->Send(protoev); + } + + public: + WriteNeighborsWithCap(User* user, ClientProtocol::Event& ev, const Cap::Capability& capability) + : cap(capability) + , protoev(ev) + { + user->ForEachNeighbor(*this, false); + } +}; + +/** Base class for simple message tags. + * Message tags provided by classes derived from this class will be sent to clients that have negotiated + * a client capability, also managed by this class. + * + * Derived classes specify the name of the capability and the message tag and provide a public GetValue() + * method with the following signature: const std::string* GetValue(ClientProtocol::Message& msg). + * The returned value determines whether to attach the tag to the message. If it is NULL, the tag won't + * be attached. If it is non-NULL the tag will be attached with the value in the string. If the string is + * empty the tag is attached without a value. + * + * Providers inheriting from this class don't accept incoming tags by default. + * + * For more control, inherit from ClientProtocol::MessageTagProvider directly. + * + * Template parameter T is the derived class. + */ +template <typename T> +class IRCv3::CapTag : public ClientProtocol::MessageTagProvider +{ + Cap::Capability cap; + const std::string tagname; + + bool ShouldSendTag(LocalUser* user, const ClientProtocol::MessageTagData& tagdata) CXX11_OVERRIDE + { + return cap.get(user); + } + + void OnClientProtocolPopulateTags(ClientProtocol::Message& msg) CXX11_OVERRIDE + { + T& tag = static_cast<T&>(*this); + const std::string* const val = tag.GetValue(msg); + if (val) + msg.AddTag(tagname, this, *val); + } + + public: + /** Constructor. + * @param mod Module that owns the tag. + * @param capname Name of the client capability. + * A client capability with this name will be created. It will be available to all clients and it won't + * have a value. + * See Cap::Capability for more info on client capabilities. + * @param Tagname Name of the message tag, to use in the protocol. + */ + CapTag(Module* mod, const std::string& capname, const std::string& Tagname) + : ClientProtocol::MessageTagProvider(mod) + , cap(mod, capname) + , tagname(Tagname) + { + } +}; diff --git a/include/modules/ircv3_batch.h b/include/modules/ircv3_batch.h new file mode 100644 index 000000000..841554bdb --- /dev/null +++ b/include/modules/ircv3_batch.h @@ -0,0 +1,185 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com> + * + * 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/>. + */ + + +#pragma once + +// For CapReference +#include "modules/cap.h" + +namespace IRCv3 +{ + namespace Batch + { + typedef uint64_t RefTag; + class Manager; + class ManagerImpl; + class Batch; + struct BatchInfo; + class API; + class CapReference; + + static const unsigned int MAX_BATCHES = (sizeof(intptr_t) * 8) - 1; + } +} + +/** Batch Manager. + * Implements batch starting and stopping. When it becomes unavailable (due to e.g. module unload) + * all running batches are stopped. + */ +class IRCv3::Batch::Manager : public DataProvider, public ClientProtocol::MessageTagProvider +{ + public: + /** Constructor. + * @param mod Module that owns the Manager. + */ + Manager(Module* mod) + : DataProvider(mod, "batchapi") + , ClientProtocol::MessageTagProvider(mod) + { + } + + /** Start a batch. + * Check Batch::IsRunning() to learn if the batch has been started. + * @param batch Batch to start. + */ + virtual void Start(Batch& batch) = 0; + + /** End a batch. + * @param batch Batch to end. + */ + virtual void End(Batch& batch) = 0; +}; + +/** Represents a batch. + * Batches are used to group together physically separate client protocol messages that logically belong + * together for one reason or another. The type of a batch, if provided, indicates what kind of grouping + * it does. + * + * Batch objects have two states: running and stopped. If a batch is running, messages can be added to it. + * If a message has been added to a batch and that message is sent to a client that negotiated the batch + * capability then the client will receive a message tag attached to the message indicating the batch that + * the message is a part of. If a message M is part of a batch B and M is sent to a client that hasn't yet + * received any message from batch B it will get a batch start message for B before M. When a batch B is + * stopped, every client that received at least one message which was in batch B will receive an end of + * batch message for B. + * A message may only be part of a single batch at any given time. + */ +class IRCv3::Batch::Batch +{ + Manager* manager; + const std::string type; + RefTag reftag; + std::string reftagstr; + unsigned int bit; + BatchInfo* batchinfo; + ClientProtocol::Message* batchstartmsg; + + void Setup(unsigned int b) + { + bit = b; + reftag = (1 << bit); + reftagstr = ConvToStr(reftag); + } + + unsigned int GetId() const { return bit; } + intptr_t GetBit() const { return reftag; } + + public: + /** Constructor. + * The batch is initially stopped. To start it, pass it to Manager::Start(). + * @param Type Batch type string, used to indicate what kind of grouping the batch does. May be empty. + */ + Batch(const std::string& Type) + : manager(NULL) + , type(Type) + , batchinfo(NULL) + , batchstartmsg(NULL) + { + } + + /** Destructor. + * If the batch is running, it is ended. + */ + ~Batch() + { + if (manager) + manager->End(*this); + } + + /** Add a message to the batch. + * If the batch isn't running then this method does nothing. + * @param msg Message to add to the batch. If it is already part of any batch, this method is a no-op. + */ + void AddToBatch(ClientProtocol::Message& msg) + { + if (manager) + msg.AddTag("batch", manager, reftagstr, this); + } + + /** Get batch reference tag which is an opaque id for the batch and is used in the client protocol. + * Only running batches have a reference tag assigned. + * @return Reference tag as a string, only valid if the batch is running. + */ + const std::string& GetRefTagStr() const { return reftagstr; } + + /** Get batch type. + * @return Batch type string. + */ + const std::string& GetType() const { return type; } + + /** Check whether the batch is running. + * Batches can be started with Manager::Start() and stopped with Manager::End(). + * @return True if the batch is running, false otherwise. + */ + bool IsRunning() const { return (manager != NULL); } + + /** Get the batch start client protocol message. + * The returned message object can be manipulated to add extra parameters or labels to the message. The first + * parameter of the message is the batch reference tag generated by the module providing batch support. + * If the batch type string was specified, it will be the second parameter of the message. + * May only be called if IsRunning() == true. + * @return Mutable batch start client protocol message. + */ + ClientProtocol::Message& GetBatchStartMessage() { return *batchstartmsg; } + + friend class ManagerImpl; +}; + +/** Batch API. Use this to access the Manager. + */ +class IRCv3::Batch::API : public dynamic_reference_nocheck<Manager> +{ + public: + API(Module* mod) + : dynamic_reference_nocheck<Manager>(mod, "batchapi") + { + } +}; + +/** Reference to the batch cap. + * Can be used to check whether a user has the batch client cap enabled. + */ +class IRCv3::Batch::CapReference : public Cap::Reference +{ + public: + CapReference(Module* mod) + : Cap::Reference(mod, "batch") + { + } +}; diff --git a/include/modules/ircv3_servertime.h b/include/modules/ircv3_servertime.h new file mode 100644 index 000000000..b917531a0 --- /dev/null +++ b/include/modules/ircv3_servertime.h @@ -0,0 +1,88 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com> + * + * 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/>. + */ + + +#pragma once + +namespace IRCv3 +{ + namespace ServerTime + { + class Manager; + class API; + + /** Format a unix timestamp into the format used by server-time. + * @param t Time to format. + * @return Time in server-time format, as a string. + */ + inline std::string FormatTime(time_t t) + { + return InspIRCd::TimeString(t, "%Y-%m-%dT%H:%M:%S.000Z", true); + } + } +} + +/** Implements manipulating the server time on messages. + * A timestamp can be attached to outgoing client protocol messages to indicate the time when the message + * was generated by us. If a message has server time attached then recipient clients who have negotiated + * the appropriate protocol extension will receive it. + */ +class IRCv3::ServerTime::Manager : public DataProvider +{ + protected: + ClientProtocol::MessageTagProvider* tagprov; + + public: + /** Constructor. + * @param mod Module that owns the Manager. + */ + Manager(Module* mod) + : DataProvider(mod, "servertimeapi") + { + } + + /** Set the server time on a message. + * @param msg Message to set the time on. No-op if the message already has server time set. + * @param t Unix timestamp to set. + */ + void Set(ClientProtocol::Message& msg, time_t t) + { + Set(msg, FormatTime(t)); + } + + /** Set the server time on a message. + * @param msg Message to set the time on. No-op if the message already has server time set. + * @param timestr Timestamp to set. Must be in server time format. + * The FormatTime() function can be used to convert unix timestamps into the required format. + */ + void Set(ClientProtocol::Message& msg, const std::string& timestr) + { + msg.AddTag("time", tagprov, timestr); + } +}; + +/** Server time API. Use this to access the Manager. + */ +class IRCv3::ServerTime::API : public dynamic_reference_nocheck<Manager> +{ + public: + API(Module* mod) + : dynamic_reference_nocheck<Manager>(mod, "servertimeapi") + { + } +}; diff --git a/include/modules/ldap.h b/include/modules/ldap.h new file mode 100644 index 000000000..aeb3aa335 --- /dev/null +++ b/include/modules/ldap.h @@ -0,0 +1,192 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2015 Adam <Adam@anope.org> + * Copyright (C) 2003-2015 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/>. + */ + +#pragma once + +typedef int LDAPQuery; + +class LDAPException : public ModuleException +{ + public: + LDAPException(const std::string& reason) : ModuleException(reason) { } + + virtual ~LDAPException() throw() { } +}; + +struct LDAPModification +{ + enum LDAPOperation + { + LDAP_ADD, + LDAP_DEL, + LDAP_REPLACE + }; + + LDAPOperation op; + std::string name; + std::vector<std::string> values; +}; + +typedef std::vector<LDAPModification> LDAPMods; + +struct LDAPAttributes : public std::map<std::string, std::vector<std::string> > +{ + size_t size(const std::string& attr) const + { + const std::vector<std::string>& array = this->getArray(attr); + return array.size(); + } + + const std::vector<std::string> keys() const + { + std::vector<std::string> k; + for (const_iterator it = this->begin(), it_end = this->end(); it != it_end; ++it) + k.push_back(it->first); + return k; + } + + const std::string& get(const std::string& attr) const + { + const std::vector<std::string>& array = this->getArray(attr); + if (array.empty()) + throw LDAPException("Empty attribute " + attr + " in LDAPResult::get"); + return array[0]; + } + + const std::vector<std::string>& getArray(const std::string& attr) const + { + const_iterator it = this->find(attr); + if (it == this->end()) + throw LDAPException("Unknown attribute " + attr + " in LDAPResult::getArray"); + return it->second; + } +}; + +enum QueryType +{ + QUERY_UNKNOWN, + QUERY_BIND, + QUERY_SEARCH, + QUERY_ADD, + QUERY_DELETE, + QUERY_MODIFY, + QUERY_COMPARE +}; + +struct LDAPResult +{ + std::vector<LDAPAttributes> messages; + std::string error; + + QueryType type; + LDAPQuery id; + + LDAPResult() + : type(QUERY_UNKNOWN), id(-1) + { + } + + size_t size() const + { + return this->messages.size(); + } + + bool empty() const + { + return this->messages.empty(); + } + + const LDAPAttributes& get(size_t sz) const + { + if (sz >= this->messages.size()) + throw LDAPException("Index out of range"); + return this->messages[sz]; + } + + const std::string& getError() const + { + return this->error; + } +}; + +class LDAPInterface +{ + public: + ModuleRef creator; + + LDAPInterface(Module* m) : creator(m) { } + virtual ~LDAPInterface() { } + + virtual void OnResult(const LDAPResult& r) = 0; + virtual void OnError(const LDAPResult& err) = 0; +}; + +class LDAPProvider : public DataProvider +{ + public: + LDAPProvider(Module* Creator, const std::string& Name) + : DataProvider(Creator, Name) { } + + /** Attempt to bind to the LDAP server as a manager + * @param i The LDAPInterface the result is sent to + */ + virtual void BindAsManager(LDAPInterface* i) = 0; + + /** Bind to LDAP + * @param i The LDAPInterface the result is sent to + * @param who The binddn + * @param pass The password + */ + virtual void Bind(LDAPInterface* i, const std::string& who, const std::string& pass) = 0; + + /** Search ldap for the specified filter + * @param i The LDAPInterface the result is sent to + * @param base The base DN to search + * @param filter The filter to apply + */ + virtual void Search(LDAPInterface* i, const std::string& base, const std::string& filter) = 0; + + /** Add an entry to LDAP + * @param i The LDAPInterface the result is sent to + * @param dn The dn of the entry to add + * @param attributes The attributes + */ + virtual void Add(LDAPInterface* i, const std::string& dn, LDAPMods& attributes) = 0; + + /** Delete an entry from LDAP + * @param i The LDAPInterface the result is sent to + * @param dn The dn of the entry to delete + */ + virtual void Del(LDAPInterface* i, const std::string& dn) = 0; + + /** Modify an existing entry in LDAP + * @param i The LDAPInterface the result is sent to + * @param base The base DN to modify + * @param attributes The attributes to modify + */ + virtual void Modify(LDAPInterface* i, const std::string& base, LDAPMods& attributes) = 0; + + /** Compare an attribute in LDAP with our value + * @param i The LDAPInterface the result is sent to + * @param dn DN to use for comparing + * @param attr Attr of DN to compare with + * @param val value to compare attr of dn + */ + virtual void Compare(LDAPInterface* i, const std::string& dn, const std::string& attr, const std::string& val) = 0; +}; diff --git a/include/modules/regex.h b/include/modules/regex.h new file mode 100644 index 000000000..5ef00cdd0 --- /dev/null +++ b/include/modules/regex.h @@ -0,0 +1,62 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org> + * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.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/>. + */ + + +#pragma once + +#include "inspircd.h" + +class Regex : public classbase +{ +protected: + /** The uncompiled regex string. */ + std::string regex_string; + + // Constructor may as well be protected, as this class is abstract. + Regex(const std::string& rx) : regex_string(rx) { } + +public: + + virtual ~Regex() { } + + virtual bool Matches(const std::string& text) = 0; + + const std::string& GetRegexString() const + { + return regex_string; + } +}; + +class RegexFactory : public DataProvider +{ + public: + RegexFactory(Module* Creator, const std::string& Name) : DataProvider(Creator, Name) { } + + virtual Regex* Create(const std::string& expr) = 0; +}; + +class RegexException : public ModuleException +{ + public: + RegexException(const std::string& regex, const std::string& error) + : ModuleException("Error in regex '" + regex + "': " + error) { } + + RegexException(const std::string& regex, const std::string& error, int offset) + : ModuleException("Error in regex '" + regex + "' at offset " + ConvToStr(offset) + ": " + error) { } +}; diff --git a/include/modules/reload.h b/include/modules/reload.h new file mode 100644 index 000000000..dcdbc95e9 --- /dev/null +++ b/include/modules/reload.h @@ -0,0 +1,80 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com> + * + * 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/>. + */ + + +#pragma once + +#include "event.h" + +namespace ReloadModule +{ + class EventListener; + class DataKeeper; + + /** Container for data saved by modules before another module is reloaded. + */ + class CustomData + { + struct Data + { + EventListener* handler; + void* data; + Data(EventListener* Handler, void* moddata) : handler(Handler), data(moddata) { } + }; + typedef std::vector<Data> List; + List list; + + public: + /** Add data to the saved state of a module. + * The provided handler's OnReloadModuleRestore() method will be called when the reload is done with the pointer + * provided. + * @param handler Handler for restoring the data + * @param data Pointer to the data, will be passed back to the provided handler's OnReloadModuleRestore() after the + * reload finishes + */ + void add(EventListener* handler, void* data) + { + list.push_back(Data(handler, data)); + } + + friend class DataKeeper; + }; + + class EventListener : public Events::ModuleEventListener + { + public: + EventListener(Module* mod) + : ModuleEventListener(mod, "event/reloadmodule") + { + } + + /** Called whenever a module is about to be reloaded. Use this event to save data related to the module that you want + * to be restored after the reload. + * @param mod Module to be reloaded + * @param cd CustomData instance that can store your data once. + */ + virtual void OnReloadModuleSave(Module* mod, CustomData& cd) = 0; + + /** Restore data after a reload. Only called if data was added in OnReloadModuleSave(). + * @param mod Reloaded module, if NULL the reload failed and the module no longer exists + * @param data Pointer that was passed to CustomData::add() in OnReloadModuleSave() at the time when the module's state + * was saved + */ + virtual void OnReloadModuleRestore(Module* mod, void* data) = 0; + }; +} diff --git a/include/modes/umode_o.h b/include/modules/sasl.h index f9644a097..8a54cfdb3 100644 --- a/include/modes/umode_o.h +++ b/include/modules/sasl.h @@ -1,7 +1,7 @@ /* * InspIRCd -- Internet Relay Chat Daemon * - * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org> + * Copyright (C) 2010 Daniel De Graaf <danieldg@inspircd.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 @@ -17,15 +17,17 @@ */ -#include "mode.h" +#pragma once -class InspIRCd; +#include "event.h" -/** User mode +o - */ -class ModeUserOperator : public ModeHandler +class SASLEventListener : public Events::ModuleEventListener { public: - ModeUserOperator(); - ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding); + SASLEventListener(Module* mod) + : ModuleEventListener(mod, "event/sasl") + { + } + + virtual void OnSASLAuth(const CommandBase::Params& params) = 0; }; diff --git a/include/modules/server.h b/include/modules/server.h new file mode 100644 index 000000000..99bd2ee1d --- /dev/null +++ b/include/modules/server.h @@ -0,0 +1,61 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.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/>. + */ + + +#pragma once + +#include "event.h" + +class ServerEventListener : public Events::ModuleEventListener +{ + public: + ServerEventListener(Module* mod) + : ModuleEventListener(mod, "event/server") + { + } + + /** Fired when a server finishes burst + * @param server Server that recently linked and finished burst + */ + virtual void OnServerLink(const Server* server) { } + + /** Fired when a server splits + * @param server Server that split + */ + virtual void OnServerSplit(const Server* server) { } + + /** Allows modules to synchronize user metadata during a netburst. This will + * be called for every user visible on your side of the burst. + * @param user The user being synchronized. + * @param server The target of the burst. + */ + virtual void OnSyncUser(User* user, ProtocolServer& server) { } + + /** Allows modules to synchronize channel metadata during a netburst. This will + * be called for every channel visible on your side of the burst. + * @param chan The channel being synchronized. + * @param server The target of the burst. + */ + virtual void OnSyncChannel(Channel* chan, ProtocolServer& server) { } + + /** Allows modules to synchronize network metadata during a netburst. + * @param server The target of the burst. + */ + virtual void OnSyncNetwork(ProtocolServer& server) { } + +}; diff --git a/include/modules/shun.h b/include/modules/shun.h new file mode 100644 index 000000000..9ce547e2d --- /dev/null +++ b/include/modules/shun.h @@ -0,0 +1,70 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2017 Dylan Frank <b00mx0r@aureus.pw> + * + * 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/>. + */ + + +#pragma once + +#include "xline.h" + +/** Shun class + */ +class Shun : public XLine +{ + public: + /** Create a Shun. + * @param s_time The set time + * @param d The duration of the xline + * @param src The sender of the xline + * @param re The reason of the xline + * @param shunmask Mask to match + */ + Shun(time_t s_time, long d, const std::string& src, const std::string& re, const std::string& shunmask) + : XLine(s_time, d, src, re, "SHUN") + , matchtext(shunmask) + { + } + + bool Matches(User* u) CXX11_OVERRIDE + { + LocalUser* lu = IS_LOCAL(u); + if (lu && lu->exempt) + return false; + + if (InspIRCd::Match(u->GetFullHost(), matchtext) || InspIRCd::Match(u->GetFullRealHost(), matchtext) || InspIRCd::Match(u->nick+"!"+u->ident+"@"+u->GetIPString(), matchtext)) + return true; + + if (InspIRCd::MatchCIDR(u->GetIPString(), matchtext, ascii_case_insensitive_map)) + return true; + + return false; + } + + bool Matches(const std::string& str) CXX11_OVERRIDE + { + return (matchtext == str); + } + + const std::string& Displayable() CXX11_OVERRIDE + { + return matchtext; + } + + private: + /** Matching mask **/ + std::string matchtext; +}; diff --git a/include/modules/sql.h b/include/modules/sql.h new file mode 100644 index 000000000..15e8260b6 --- /dev/null +++ b/include/modules/sql.h @@ -0,0 +1,267 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2017 Peter Powell <petpow@saberuk.com> + * Copyright (C) 2010 Daniel De Graaf <danieldg@inspircd.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/>. + */ + + +#pragma once + + +namespace SQL +{ + class Error; + class Field; + class Provider; + class Query; + class Result; + + /** A list of parameter replacement values. */ + typedef std::vector<std::string> ParamList; + + /** A map of parameter replacement values. */ + typedef std::map<std::string, std::string> ParamMap; + + /** A list of SQL fields from a specific row. */ + typedef std::vector<Field> Row; + + /** An enumeration of possible error codes. */ + enum ErrorCode + { + /** No error has occurred. */ + SUCCESS, + + /** The database identifier is invalid. */ + BAD_DBID, + + /** The database connection has failed. */ + BAD_CONN, + + /** Executing the query failed. */ + QSEND_FAIL, + + /** Reading the response failed. */ + QREPLY_FAIL + }; + + /** Populates a parameter map with information about a user. + * @param user The user to collect information from. + * @param userinfo The map to populate. + */ + void PopulateUserInfo(User* user, ParamMap& userinfo); +} + +/** Represents a single SQL field. */ +class SQL::Field +{ + private: + /** Whether this SQL field is NULL. */ + bool null; + + /** The underlying SQL value. */ + std::string value; + + public: + /** Creates a new NULL SQL field. */ + Field() + : null(true) + { + } + + /** Creates a new non-NULL SQL field. + * @param v The value of the field. + */ + Field(const std::string& v) + : null(false) + , value(v) + { + } + + /** Determines whether this SQL entry is NULL. */ + inline bool IsNull() const { return null; } + + /** Retrieves the underlying value. */ + inline operator const std::string&() const { return value; } +}; + +/** Represents the result of an SQL query. */ +class SQL::Result : public classbase +{ + public: + /** + * Return the number of rows in the result. + * + * Note that if you have perfomed an INSERT or UPDATE query or other + * query which will not return rows, this will return the number of + * affected rows. In this case you SHOULD NEVER access any of the result + * set rows, as there aren't any! + * @returns Number of rows in the result set. + */ + virtual int Rows() = 0; + + /** Retrieves the next available row from the database. + * @param result A list to store the fields from this row in. + * @return True if a row could be retrieved; otherwise, false. + */ + virtual bool GetRow(Row& result) = 0; + + /** Retrieves a list of SQL columns in the result. + * @param result A reference to the vector to store column names in. + */ + virtual void GetCols(std::vector<std::string>& result) = 0; + + /** + * Check if there's a column with the specified name in the result + * + * @param the column name + * @param on success, this is the column index + * @returns true, or false if the column is not found + */ + virtual bool HasColumn(const std::string& column, size_t& index) = 0; +}; + +/** SQL::Error holds the error state of a request. + * The error string varies from database software to database software + * and should be used to display informational error messages to users. + */ +class SQL::Error +{ + private: + /** The custom error message if one has been specified. */ + const std::string message; + + public: + /** The code which represents this error. */ + const ErrorCode code; + + /** Initialize an SQL::Error from an error code. + * @param c A code which represents this error. + */ + Error(ErrorCode c) + : code(c) + { + } + + /** Initialize an SQL::Error from an error code and a custom error message. + * @param c A code which represents this error. + * @param m A custom error message. + */ + Error(ErrorCode c, const std::string m) + : message(m) + , code(c) + { + } + + /** Retrieves the error message. */ + const char* ToString() const + { + if (!message.empty()) + return message.c_str(); + + switch (code) + { + case BAD_DBID: + return "Invalid database identifier"; + case BAD_CONN: + return "Invalid connection"; + case QSEND_FAIL: + return "Sending query failed"; + case QREPLY_FAIL: + return "Getting query result failed"; + default: + return "Unknown error"; + } + } +}; + +/** + * Object representing an SQL query. This should be allocated on the heap and + * passed to an SQL::Provider, which will free it when the query is complete or + * when the querying module is unloaded. + * + * You should store whatever information is needed to have the callbacks work in + * this object (UID of user, channel name, etc). + */ +class SQL::Query : public classbase +{ + protected: + /** Creates a new SQL query. */ + Query(Module* Creator) + : creator(Creator) + { + } + + public: + const ModuleRef creator; + + /* Destroys this Query instance. */ + virtual ~Query() + { + } + + /** Called when an SQL error happens. + * @param error The error that occurred. + */ + virtual void OnError(Error& error) = 0; + + /** Called when a SQL result is received. + * @param result The result of the SQL query. + */ + virtual void OnResult(Result& result) = 0; +}; + +/** + * Provider object for SQL servers + */ +class SQL::Provider : public DataProvider +{ + public: + Provider(Module* Creator, const std::string& Name) + : DataProvider(Creator, Name) + { + } + + /** Submit an asynchronous SQL query. + * @param callback The result reporting point + * @param query The hardcoded query string. If you have parameters to substitute, see below. + */ + virtual void Submit(Query* callback, const std::string& query) = 0; + + /** Submit an asynchronous SQL query. + * @param callback The result reporting point + * @param format The simple parameterized query string ('?' parameters) + * @param p Parameters to fill in for the '?' entries + */ + virtual void Submit(Query* callback, const std::string& format, const SQL::ParamList& p) = 0; + + /** Submit an asynchronous SQL query. + * @param callback The result reporting point + * @param format The parameterized query string ('$name' parameters) + * @param p Parameters to fill in for the '$name' entries + */ + virtual void Submit(Query* callback, const std::string& format, const ParamMap& p) = 0; +}; + +inline void SQL::PopulateUserInfo(User* user, ParamMap& userinfo) +{ + userinfo["nick"] = user->nick; + userinfo["host"] = user->GetRealHost(); + userinfo["ip"] = user->GetIPString(); + userinfo["real"] = user->GetRealName(); + userinfo["ident"] = user->ident; + userinfo["server"] = user->server->GetName(); + userinfo["uuid"] = user->uuid; +} diff --git a/include/modules/ssl.h b/include/modules/ssl.h new file mode 100644 index 000000000..930cb6dc6 --- /dev/null +++ b/include/modules/ssl.h @@ -0,0 +1,305 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org> + * Copyright (C) 2006 Craig Edwards <craigedwards@brainbox.cc> + * + * 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/>. + */ + + +#pragma once + +#include <string> +#include "iohook.h" + +/** ssl_cert is a class which abstracts SSL certificate + * and key information. + * + * Because gnutls and openssl represent key information in + * wildly different ways, this class allows it to be accessed + * in a unified manner. These classes are attached to ssl- + * connected local users using SSLCertExt + */ +class ssl_cert : public refcountbase +{ + public: + std::string dn; + std::string issuer; + std::string error; + std::string fingerprint; + bool trusted, invalid, unknownsigner, revoked; + + ssl_cert() : trusted(false), invalid(true), unknownsigner(true), revoked(false) {} + + /** Get certificate distinguished name + * @return Certificate DN + */ + const std::string& GetDN() + { + return dn; + } + + /** Get Certificate issuer + * @return Certificate issuer + */ + const std::string& GetIssuer() + { + return issuer; + } + + /** Get error string if an error has occured + * @return The error associated with this users certificate, + * or an empty string if there is no error. + */ + const std::string& GetError() + { + return error; + } + + /** Get key fingerprint. + * @return The key fingerprint as a hex string. + */ + const std::string& GetFingerprint() + { + return fingerprint; + } + + /** Get trust status + * @return True if this is a trusted certificate + * (the certificate chain validates) + */ + bool IsTrusted() + { + return trusted; + } + + /** Get validity status + * @return True if the certificate itself is + * correctly formed. + */ + bool IsInvalid() + { + return invalid; + } + + /** Get signer status + * @return True if the certificate appears to be + * self-signed. + */ + bool IsUnknownSigner() + { + return unknownsigner; + } + + /** Get revokation status. + * @return True if the certificate is revoked. + * Note that this only works properly for GnuTLS + * right now. + */ + bool IsRevoked() + { + return revoked; + } + + /** Get certificate usability + * @return True if the certificate is not expired nor revoked + */ + bool IsUsable() + { + return !invalid && !revoked && error.empty(); + } + + /** Get CA trust status + * @return True if the certificate is issued by a CA + * and valid. + */ + bool IsCAVerified() + { + return IsUsable() && trusted && !unknownsigner; + } + + std::string GetMetaLine() + { + std::stringstream value; + bool hasError = !error.empty(); + value << (IsInvalid() ? "v" : "V") << (IsTrusted() ? "T" : "t") << (IsRevoked() ? "R" : "r") + << (IsUnknownSigner() ? "s" : "S") << (hasError ? "E" : "e") << " "; + if (hasError) + value << GetError(); + else + value << GetFingerprint() << " " << GetDN() << " " << GetIssuer(); + return value.str(); + } +}; + +class SSLIOHook : public IOHook +{ + protected: + /** Peer SSL certificate, set by the SSL module + */ + reference<ssl_cert> certificate; + + /** Reduce elements in a send queue by appending later elements to the first element until there are no more + * elements to append or a desired length is reached + * @param sendq SendQ to work on + * @param targetsize Target size of the front element + */ + static void FlattenSendQueue(StreamSocket::SendQueue& sendq, size_t targetsize) + { + if ((sendq.size() <= 1) || (sendq.front().length() >= targetsize)) + return; + + // Avoid multiple repeated SSL encryption invocations + // This adds a single copy of the queue, but avoids + // much more overhead in terms of system calls invoked + // by an IOHook. + std::string tmp; + tmp.reserve(std::min(targetsize, sendq.bytes())+1); + do + { + tmp.append(sendq.front()); + sendq.pop_front(); + } + while (!sendq.empty() && tmp.length() < targetsize); + sendq.push_front(tmp); + } + + public: + static SSLIOHook* IsSSL(StreamSocket* sock) + { + IOHook* const iohook = sock->GetIOHook(); + if ((iohook) && ((iohook->prov->type == IOHookProvider::IOH_SSL))) + return static_cast<SSLIOHook*>(iohook); + return NULL; + } + + SSLIOHook(IOHookProvider* hookprov) + : IOHook(hookprov) + { + } + + /** + * Get the certificate sent by this peer + * @return The SSL certificate sent by the peer, NULL if no cert was sent + */ + ssl_cert* GetCertificate() const + { + if (certificate && certificate->IsUsable()) + return certificate; + return NULL; + } + + /** + * Get the fingerprint of the peer's certificate + * @return The fingerprint of the SSL client certificate sent by the peer, + * empty if no cert was sent + */ + std::string GetFingerprint() const + { + ssl_cert* cert = GetCertificate(); + if (cert) + return cert->GetFingerprint(); + return ""; + } + + /** + * Get the ciphersuite negotiated with the peer + * @param out String where the ciphersuite string will be appended to + */ + virtual void GetCiphersuite(std::string& out) const = 0; + + + /** Retrieves the name of the SSL connection which is sent via SNI. + * @param out String that the server name will be appended to. + * returns True if the server name was retrieved; otherwise, false. + */ + virtual bool GetServerName(std::string& out) const = 0; +}; + +/** Helper functions for obtaining SSL client certificates and key fingerprints + * from StreamSockets + */ +class SSLClientCert +{ + public: + /** + * Get the client certificate from a socket + * @param sock The socket to get the certificate from, the socket does not have to use SSL + * @return The SSL client certificate information, NULL if the peer is not using SSL + */ + static ssl_cert* GetCertificate(StreamSocket* sock) + { + SSLIOHook* ssliohook = SSLIOHook::IsSSL(sock); + if (!ssliohook) + return NULL; + + return ssliohook->GetCertificate(); + } + + /** + * Get the fingerprint of a client certificate from a socket + * @param sock The socket to get the certificate fingerprint from, the + * socket does not have to use SSL + * @return The key fingerprint from the SSL certificate sent by the peer, + * empty if no cert was sent or the peer is not using SSL + */ + static std::string GetFingerprint(StreamSocket* sock) + { + ssl_cert* cert = SSLClientCert::GetCertificate(sock); + if (cert) + return cert->GetFingerprint(); + return ""; + } +}; + +class UserCertificateAPIBase : public DataProvider +{ + public: + UserCertificateAPIBase(Module* parent) + : DataProvider(parent, "m_sslinfo_api") + { + } + + /** Get the SSL certificate of a user + * @param user The user whose certificate to get, user may be remote + * @return The SSL certificate of the user or NULL if the user is not using SSL + */ + virtual ssl_cert* GetCertificate(User* user) = 0; + + /** Get the key fingerprint from a user's certificate + * @param user The user whose key fingerprint to get, user may be remote + * @return The key fingerprint from the user's SSL certificate or an empty string + * if the user is not using SSL or did not provide a client certificate + */ + std::string GetFingerprint(User* user) + { + ssl_cert* cert = GetCertificate(user); + if (cert) + return cert->GetFingerprint(); + return ""; + } +}; + +/** API implemented by m_sslinfo that allows modules to retrive the SSL certificate + * information of local and remote users. It can also be used to find out whether a + * user is using SSL or not. + */ +class UserCertificateAPI : public dynamic_reference<UserCertificateAPIBase> +{ + public: + UserCertificateAPI(Module* parent) + : dynamic_reference<UserCertificateAPIBase>(parent, "m_sslinfo_api") + { + } +}; diff --git a/include/modules/stats.h b/include/modules/stats.h new file mode 100644 index 000000000..e69070c9f --- /dev/null +++ b/include/modules/stats.h @@ -0,0 +1,191 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com> + * + * 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/>. + */ + + +#pragma once + +#include "event.h" + +namespace Stats +{ + class Context; + class EventListener; + class Row; +} + +class Stats::EventListener : public Events::ModuleEventListener +{ + public: + EventListener(Module* mod) + : ModuleEventListener(mod, "event/stats") + { + } + + /** Called when the STATS command is executed. + * @param stats Context of the /STATS request, contains requesting user, list of answer rows etc. + * @return MOD_RES_DENY if the stats request has been fulfilled. Otherwise, MOD_RES_PASSTHRU. + */ + virtual ModResult OnStats(Stats::Context& stats) = 0; +}; + +class Stats::Row : public Numeric::Numeric +{ + public: + Row(unsigned int num) + : Numeric(num) + { + } +}; + +class Stats::Context +{ + /** Source user of the STATS request + */ + User* const source; + + /** List of reply rows + */ + std::vector<Row> rows; + + /** Symbol indicating the type of this STATS request (usually a letter) + */ + const char symbol; + + public: + /** Constructor + * @param src Source user of the STATS request, can be a local or remote user + * @param sym Symbol (letter) indicating the type of the request + */ + Context(User* src, char sym) + : source(src) + , symbol(sym) + { + } + + /** Get the source user of the STATS request + * @return Source user of the STATS request + */ + User* GetSource() const { return source; } + + /** Get the list of reply rows + * @return List of rows generated as reply for the request + */ + const std::vector<Row>& GetRows() const { return rows; } + + /** Get the symbol (letter) indicating what type of STATS was requested + * @return Symbol specified by the requesting user + */ + char GetSymbol() const { return symbol; } + + /** Add a row to the reply list + * @param row Reply to add + */ + void AddRow(const Row& row) { rows.push_back(row); } + + template <typename T1> + void AddRow(unsigned int numeric, T1 p1) + { + Row n(numeric); + n.push(p1); + AddRow(n); + } + + template <typename T1, typename T2> + void AddRow(unsigned int numeric, T1 p1, T2 p2) + { + Row n(numeric); + n.push(p1); + n.push(p2); + AddRow(n); + } + + template <typename T1, typename T2, typename T3> + void AddRow(unsigned int numeric, T1 p1, T2 p2, T3 p3) + { + Row n(numeric); + n.push(p1); + n.push(p2); + n.push(p3); + AddRow(n); + } + + template <typename T1, typename T2, typename T3, typename T4> + void AddRow(unsigned int numeric, T1 p1, T2 p2, T3 p3, T4 p4) + { + Row n(numeric); + n.push(p1); + n.push(p2); + n.push(p3); + n.push(p4); + AddRow(n); + } + + template <typename T1, typename T2, typename T3, typename T4, typename T5> + void AddRow(unsigned int numeric, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) + { + Row n(numeric); + n.push(p1); + n.push(p2); + n.push(p3); + n.push(p4); + n.push(p5); + AddRow(n); + } + + template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6> + void AddRow(unsigned int numeric, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6) + { + Row n(numeric); + n.push(p1); + n.push(p2); + n.push(p3); + n.push(p4); + n.push(p5); + n.push(p6); + AddRow(n); + } + + template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7> + void AddRow(unsigned int numeric, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6, T7 p7) + { + Row n(numeric); + n.push(p1); + n.push(p2); + n.push(p3); + n.push(p4); + n.push(p5); + n.push(p6); + n.push(p7); + AddRow(n); + } + + template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8> + void AddRow(unsigned int numeric, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6, T7 p7, T8 p8) + { + Row n(numeric); + n.push(p1); + n.push(p2); + n.push(p3); + n.push(p4); + n.push(p5); + n.push(p6); + n.push(p7); + n.push(p8); + AddRow(n); + } +}; diff --git a/include/modules/whois.h b/include/modules/whois.h new file mode 100644 index 000000000..4f09d268b --- /dev/null +++ b/include/modules/whois.h @@ -0,0 +1,146 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com> + * + * 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/>. + */ + + +#pragma once + +#include "event.h" + +namespace Whois +{ + class EventListener; + class LineEventListener; + class Context; +} + +class Whois::EventListener : public Events::ModuleEventListener +{ + public: + EventListener(Module* mod) + : ModuleEventListener(mod, "event/whois") + { + } + + /** Called whenever a /WHOIS is performed by a local user. + * @param whois Whois context, can be used to send numerics + */ + virtual void OnWhois(Context& whois) = 0; +}; + +class Whois::LineEventListener : public Events::ModuleEventListener +{ + public: + LineEventListener(Module* mod) + : ModuleEventListener(mod, "event/whoisline") + { + } + + /** Called whenever a line of WHOIS output is sent to a user. + * You may change the numeric and the text of the output by changing + * the values numeric and text, but you cannot change the user the + * numeric is sent to. + * @param whois Whois context, can be used to send numerics + * @param numeric Numeric being sent + * @return MOD_RES_DENY to drop the line completely so that the user does not + * receive it, or MOD_RES_PASSTHRU to allow the line to be sent. + */ + virtual ModResult OnWhoisLine(Context& whois, Numeric::Numeric& numeric) = 0; +}; + +class Whois::Context +{ + protected: + /** User doing the WHOIS + */ + LocalUser* const source; + + /** User being WHOISed + */ + User* const target; + + public: + Context(LocalUser* src, User* targ) + : source(src) + , target(targ) + { + } + + /** Returns true if the user is /WHOISing himself + * @return True if whois source is the same user as the whois target, false if they are different users + */ + bool IsSelfWhois() const { return (source == target); } + + /** Returns the LocalUser who has done the /WHOIS + * @return LocalUser doing the /WHOIS + */ + LocalUser* GetSource() const { return source; } + + /** Returns the target of the /WHOIS + * @return User who was /WHOIS'd + */ + User* GetTarget() const { return target; } + + /** Send a line of WHOIS data to the source of the WHOIS + */ + template <typename T1> + void SendLine(unsigned int numeric, T1 p1) + { + Numeric::Numeric n(numeric); + n.push(target->nick); + n.push(p1); + SendLine(n); + } + + template <typename T1, typename T2> + void SendLine(unsigned int numeric, T1 p1, T2 p2) + { + Numeric::Numeric n(numeric); + n.push(target->nick); + n.push(p1); + n.push(p2); + SendLine(n); + } + + template <typename T1, typename T2, typename T3> + void SendLine(unsigned int numeric, T1 p1, T2 p2, T3 p3) + { + Numeric::Numeric n(numeric); + n.push(target->nick); + n.push(p1); + n.push(p2); + n.push(p3); + SendLine(n); + } + + template <typename T1, typename T2, typename T3, typename T4> + void SendLine(unsigned int numeric, T1 p1, T2 p2, T3 p3, T4 p4) + { + Numeric::Numeric n(numeric); + n.push(target->nick); + n.push(p1); + n.push(p2); + n.push(p3); + n.push(p4); + SendLine(n); + } + + /** Send a line of WHOIS data to the source of the WHOIS + * @param numeric Numeric to send + */ + virtual void SendLine(Numeric::Numeric& numeric) = 0; +}; diff --git a/include/numeric.h b/include/numeric.h new file mode 100644 index 000000000..cc9f9cc9b --- /dev/null +++ b/include/numeric.h @@ -0,0 +1,87 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com> + * + * 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/>. + */ + + +#pragma once + +#include "numerics.h" + +namespace Numeric +{ + class Numeric; +} + +class Numeric::Numeric +{ + /** Numeric number + */ + unsigned int numeric; + + /** Parameters of the numeric + */ + CommandBase::Params params; + + /** Source server of the numeric, if NULL (the default) then it is the local server + */ + Server* sourceserver; + + public: + /** Constructor + * @param num Numeric number (RPL_*, ERR_*) + */ + Numeric(unsigned int num) + : numeric(num) + , sourceserver(NULL) + { + } + + /** Add a parameter to the numeric. The parameter will be converted to a string first with ConvToStr(). + * @param x Parameter to add + */ + template <typename T> + Numeric& push(const T& x) + { + params.push_back(ConvToStr(x)); + return *this; + } + + /** Set the source server of the numeric. The source server defaults to the local server. + * @param server Server to set as source + */ + void SetServer(Server* server) { sourceserver = server; } + + /** Get the source server of the numeric + * @return Source server or NULL if the source is the local server + */ + Server* GetServer() const { return sourceserver; } + + /** Get the number of the numeric as an unsigned integer + * @return Numeric number as an unsigned integer + */ + unsigned int GetNumeric() const { return numeric; } + + /** Get the parameters of the numeric + * @return Parameters of the numeric as a const vector of strings + */ + const CommandBase::Params& GetParams() const { return params; } + + /** Get the parameters of the numeric + * @return Parameters of the numeric as a vector of strings + */ + CommandBase::Params& GetParams() { return params; } +}; diff --git a/include/numericbuilder.h b/include/numericbuilder.h new file mode 100644 index 000000000..0d55093ca --- /dev/null +++ b/include/numericbuilder.h @@ -0,0 +1,252 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2015-2016 Attila Molnar <attilamolnar@hush.com> + * + * 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/>. + */ + + +#pragma once + +namespace Numeric +{ + class WriteNumericSink; + class WriteRemoteNumericSink; + + template <char Sep, bool SendEmpty, typename Sink> + class GenericBuilder; + + template <char Sep = ',', bool SendEmpty = false> + class Builder; + + template <unsigned int NumStaticParams, bool SendEmpty, typename Sink> + class GenericParamBuilder; + + template <unsigned int NumStaticParams, bool SendEmpty = false> + class ParamBuilder; +} + +class Numeric::WriteNumericSink +{ + LocalUser* const user; + + public: + WriteNumericSink(LocalUser* u) + : user(u) + { + } + + void operator()(Numeric& numeric) const + { + user->WriteNumeric(numeric); + } +}; + +class Numeric::WriteRemoteNumericSink +{ + User* const user; + + public: + WriteRemoteNumericSink(User* u) + : user(u) + { + } + + void operator()(Numeric& numeric) const + { + user->WriteRemoteNumeric(numeric); + } +}; + +template <char Sep, bool SendEmpty, typename Sink> +class Numeric::GenericBuilder +{ + Sink sink; + Numeric numeric; + const std::string::size_type max; + + bool HasRoom(const std::string::size_type additional) const + { + return (numeric.GetParams().back().size() + additional <= max); + } + + public: + GenericBuilder(Sink s, unsigned int num, bool addparam = true, size_t additionalsize = 0) + : sink(s) + , numeric(num) + , max(ServerInstance->Config->Limits.MaxLine - ServerInstance->Config->ServerName.size() - additionalsize - 10) + { + if (addparam) + numeric.push(std::string()); + } + + Numeric& GetNumeric() { return numeric; } + + void Add(const std::string& entry) + { + if (!HasRoom(entry.size())) + Flush(); + numeric.GetParams().back().append(entry).push_back(Sep); + } + + void Add(const std::string& entry1, const std::string& entry2) + { + if (!HasRoom(entry1.size() + entry2.size())) + Flush(); + numeric.GetParams().back().append(entry1).append(entry2).push_back(Sep); + } + + void Flush() + { + std::string& data = numeric.GetParams().back(); + if (IsEmpty()) + { + if (!SendEmpty) + return; + } + else + { + data.erase(data.size()-1); + } + + sink(numeric); + data.clear(); + } + + bool IsEmpty() const { return (numeric.GetParams().back().empty()); } +}; + +template <char Sep, bool SendEmpty> +class Numeric::Builder : public GenericBuilder<Sep, SendEmpty, WriteNumericSink> +{ + public: + Builder(LocalUser* user, unsigned int num, bool addparam = true, size_t additionalsize = 0) + : ::Numeric::GenericBuilder<Sep, SendEmpty, WriteNumericSink>(WriteNumericSink(user), num, addparam, additionalsize + user->nick.size()) + { + } +}; + +template <unsigned int NumStaticParams, bool SendEmpty, typename Sink> +class Numeric::GenericParamBuilder +{ + Sink sink; + Numeric numeric; + std::string::size_type currlen; + std::string::size_type max; + + bool HasRoom(const std::string::size_type additional) const + { + return (currlen + additional <= max); + } + + public: + GenericParamBuilder(Sink s, unsigned int num, size_t additionalsize) + : sink(s) + , numeric(num) + , currlen(0) + , max(ServerInstance->Config->Limits.MaxLine - ServerInstance->Config->ServerName.size() - additionalsize - 10) + { + } + + void AddStatic(const std::string& entry) + { + max -= (entry.length() + 1); + numeric.GetParams().push_back(entry); + } + + void Add(const std::string& entry) + { + if (!HasRoom(entry.size())) + Flush(); + + currlen += entry.size() + 1; + numeric.GetParams().push_back(entry); + } + + void Flush() + { + if ((!SendEmpty) && (IsEmpty())) + return; + + sink(numeric); + currlen = 0; + numeric.GetParams().erase(numeric.GetParams().begin() + NumStaticParams, numeric.GetParams().end()); + } + + bool IsEmpty() const { return (numeric.GetParams().size() <= NumStaticParams); } +}; + +template <unsigned int NumStaticParams, bool SendEmpty> +class Numeric::ParamBuilder : public GenericParamBuilder<NumStaticParams, SendEmpty, WriteNumericSink> +{ + public: + ParamBuilder(LocalUser* user, unsigned int num) + : ::Numeric::GenericParamBuilder<NumStaticParams, SendEmpty, WriteNumericSink>(WriteNumericSink(user), num, user->nick.size()) + { + } +}; + +namespace Numerics +{ + class InvalidModeParameter; + class NoSuchChannel; + class NoSuchNick; +} + +/* Builder for the ERR_INVALIDMODEPARAM numeric. */ +class Numerics::InvalidModeParameter : public Numeric::Numeric +{ + public: + InvalidModeParameter(Channel* chan, ModeHandler* mode, const std::string& parameter, const std::string& message = "") + : Numeric(ERR_INVALIDMODEPARAM) + { + push(chan->name); + push(mode->GetModeChar()); + push(parameter); + push(message.empty() ? InspIRCd::Format("Invalid %s mode parameter", mode->name.c_str()) : message); + } + + InvalidModeParameter(User* user, ModeHandler* mode, const std::string& parameter, const std::string& message = "") + : Numeric(ERR_INVALIDMODEPARAM) + { + push(user->registered & REG_NICK ? user->nick : "*"); + push(mode->GetModeChar()); + push(parameter); + push(message.empty() ? InspIRCd::Format("Invalid %s mode parameter", mode->name.c_str()) : message); + } +}; + +/** Builder for the ERR_NOSUCHCHANNEL numeric. */ +class Numerics::NoSuchChannel : public Numeric::Numeric +{ + public: + NoSuchChannel(const std::string& chan) + : Numeric(ERR_NOSUCHCHANNEL) + { + push(chan); + push("No such channel"); + } +}; + +/** Builder for the ERR_NOSUCHNICK numeric. */ +class Numerics::NoSuchNick : public Numeric::Numeric +{ + public: + NoSuchNick(const std::string& nick) + : Numeric(ERR_NOSUCHNICK) + { + push(nick); + push("No such nick"); + } +}; diff --git a/include/numerics.h b/include/numerics.h index 4fce4cb6d..343f88252 100644 --- a/include/numerics.h +++ b/include/numerics.h @@ -18,18 +18,12 @@ */ -#ifndef NUMERICS_H -#define NUMERICS_H +#pragma once /* - * This file is aimed providing a string that is easier to use than using the numeric - * directly. - * * Module authors, please note! * While you are free to use any numerics on this list, like the rest of the core, you * *should not* be editing it! - * You should just WriteNumeric(444, .. or whatever as you would before this file, OR: - * #define RPL_MYNUMERIC 444 & WriteNumeric(RPL_MYNUMERIC, ... * * If you *do* have a suggestion for a numeric you genuinely believe would be useful, * please speak to us. :) @@ -39,81 +33,94 @@ * Please note that the list may not be exhaustive, it'll be done when I have * nothing better to do with my time. -- w00t (jul 13, 2008) */ -enum Numerics +enum { - /* - * Reply range of numerics. - */ - RPL_WELCOME = 1, // 2812, not 1459 - RPL_YOURHOSTIS = 2, // 2812, not 1459 - RPL_SERVERCREATED = 3, // 2812, not 1459 - RPL_SERVERVERSION = 4, // 2812, not 1459 - RPL_ISUPPORT = 5, // not RFC, extremely common though (defined as RPL_BOUNCE in 2812, widely ignored) - - RPL_MAP = 6, // unrealircd - RPL_ENDMAP = 7, // unrealircd - RPL_SNOMASKIS = 8, // unrealircd - - RPL_YOURUUID = 42, // taken from ircnet - - RPL_UMODEIS = 221, - RPL_RULES = 232, // unrealircd - RPL_ADMINME = 256, - RPL_ADMINLOC1 = 257, - RPL_ADMINLOC2 = 258, - RPL_ADMINEMAIL = 259, - RPL_MAPUSERS = 270, // insp-specific - - RPL_SYNTAX = 304, // insp-specific - - RPL_UNAWAY = 305, - RPL_NOWAWAY = 306, - - RPL_RULESTART = 308, // unrealircd - RPL_RULESEND = 309, // unrealircd - RPL_CHANNELMODEIS = 324, - RPL_CHANNELCREATED = 329, // ??? - RPL_NOTOPICSET = 331, - RPL_TOPIC = 332, - RPL_TOPICTIME = 333, // not RFC, extremely common though - - RPL_INVITING = 341, - RPL_INVITELIST = 346, // insp-specific (stolen from ircu) - RPL_ENDOFINVITELIST = 347, // insp-specific (stolen from ircu) - RPL_VERSION = 351, - RPL_NAMREPLY = 353, - RPL_ENDOFNAMES = 366, - - RPL_INFO = 371, - RPL_ENDOFINFO = 374, - RPL_MOTD = 372, - RPL_MOTDSTART = 375, - RPL_ENDOFMOTD = 376, - - RPL_YOUAREOPER = 381, - RPL_REHASHING = 382, - RPL_TIME = 391, - RPL_YOURDISPLAYEDHOST = 396, // from charybdis/etc, common convention + RPL_ISUPPORT = 5, // not RFC, extremely common though (defined as RPL_BOUNCE in 2812, widely ignored) + + RPL_SNOMASKIS = 8, // unrealircd + + RPL_MAP = 15, // ircu + RPL_ENDMAP = 17, // ircu + RPL_MAPUSERS = 18, // insp-specific + + RPL_UMODEIS = 221, + + RPL_LUSERCLIENT = 251, + RPL_LUSEROP = 252, + RPL_LUSERUNKNOWN = 253, + RPL_LUSERCHANNELS = 254, + RPL_LUSERME = 255, + + RPL_ADMINME = 256, + RPL_ADMINLOC1 = 257, + RPL_ADMINLOC2 = 258, + RPL_ADMINEMAIL = 259, + + RPL_LOCALUSERS = 265, + RPL_GLOBALUSERS = 266, + + RPL_AWAY = 301, + RPL_USERHOST = 302, + RPL_ISON = 303, + + RPL_WHOISSERVER = 312, + RPL_ENDOFWHOIS = 318, + + RPL_LISTSTART = 321, + RPL_LIST = 322, + RPL_LISTEND = 323, + + RPL_CHANNELMODEIS = 324, + RPL_CHANNELCREATED = 329, // ??? + RPL_NOTOPICSET = 331, + RPL_TOPIC = 332, + RPL_TOPICTIME = 333, // not RFC, extremely common though + + RPL_USERIP = 340, + RPL_INVITING = 341, + RPL_INVITELIST = 346, // insp-specific (stolen from ircu) + RPL_ENDOFINVITELIST = 347, // insp-specific (stolen from ircu) + RPL_VERSION = 351, + RPL_NAMREPLY = 353, + RPL_LINKS = 364, + RPL_ENDOFLINKS = 365, + RPL_ENDOFNAMES = 366, + + RPL_INFO = 371, + RPL_ENDOFINFO = 374, + RPL_MOTD = 372, + RPL_MOTDSTART = 375, + RPL_ENDOFMOTD = 376, + + RPL_YOUAREOPER = 381, + RPL_REHASHING = 382, + RPL_TIME = 391, + RPL_YOURDISPLAYEDHOST = 396, // from charybdis/etc, common convention /* * Error range of numerics. */ - ERR_NOSUCHNICK = 401, - ERR_NOSUCHSERVER = 402, - ERR_NOSUCHCHANNEL = 403, // used to indicate an invalid channel name also, so don't rely on RFC text (don't do that anyway!) - ERR_CANNOTSENDTOCHAN = 404, - ERR_TOOMANYCHANNELS = 405, - ERR_INVALIDCAPSUBCOMMAND = 410, // ratbox/charybdis(?) - ERR_UNKNOWNCOMMAND = 421, - ERR_NOMOTD = 422, - ERR_NORULES = 434, // unrealircd - ERR_USERNOTINCHANNEL = 441, - ERR_NOTONCHANNEL = 442, - ERR_USERONCHANNEL = 443, - ERR_CANTCHANGENICK = 447, // unrealircd, probably - ERR_NOTREGISTERED = 451, - ERR_NEEDMOREPARAMS = 461, - ERR_ALREADYREGISTERED = 462, + ERR_NOSUCHNICK = 401, + ERR_NOSUCHSERVER = 402, + ERR_NOSUCHCHANNEL = 403, // used to indicate an invalid channel name also, so don't rely on RFC text (don't do that anyway!) + ERR_CANNOTSENDTOCHAN = 404, + ERR_TOOMANYCHANNELS = 405, + ERR_WASNOSUCHNICK = 406, + ERR_NOTEXTTOSEND = 412, + ERR_UNKNOWNCOMMAND = 421, + ERR_NOMOTD = 422, + ERR_NONICKNAMEGIVEN = 431, + ERR_ERRONEUSNICKNAME = 432, + ERR_NICKNAMEINUSE = 433, + ERR_USERNOTINCHANNEL = 441, + ERR_NOTONCHANNEL = 442, + ERR_USERONCHANNEL = 443, + ERR_CANTCHANGENICK = 447, // unrealircd, probably + ERR_NOTREGISTERED = 451, + ERR_NEEDMOREPARAMS = 461, + ERR_ALREADYREGISTERED = 462, + ERR_YOUREBANNEDCREEP = 465, + ERR_UNKNOWNMODE = 472, /* * A quick side-rant about the next group of numerics.. @@ -131,31 +138,32 @@ enum Numerics * * -- A message from the IRC group for coder sanity, and w00t */ - ERR_BADCHANNELKEY = 475, - ERR_INVITEONLYCHAN = 473, - ERR_CHANNELISFULL = 471, - ERR_BANNEDFROMCHAN = 474, - - ERR_NOPRIVILEGES = 481, // rfc, beware though, we use this for other things opers may not do also - ERR_CHANOPRIVSNEEDED = 482, // rfc, beware though, we use this for other things like trying to kick a uline - - ERR_ALLMUSTSSL = 490, // unrealircd - ERR_NOCTCPALLOWED = 492, // XXX: bzzzz. 1459 defines this as ERR_NOSERVICEHOST, research it more and perhaps change this! (ERR_CANNOTSENDTOCHAN?) - // wtf, we also use this for m_noinvite. UGLY! - ERR_DELAYREJOIN = 495, // insp-specific, XXX: we should use 'resource temporarily unavailable' from ircnet/ratbox or whatever - ERR_UNKNOWNSNOMASK = 501, // insp-specific - ERR_USERSDONTMATCH = 502, - ERR_CANTJOINOPERSONLY = 520, // unrealircd, but crap to have so many numerics for cant join.. - ERR_CANTSENDTOUSER = 531, // ??? - - RPL_COMMANDS = 702, // insp-specific - RPL_COMMANDSEND = 703, // insp-specific - - ERR_WORDFILTERED = 936, // insp-specific, would be nice if we could get rid of this.. - ERR_CANTUNLOADMODULE = 972, // insp-specific - RPL_UNLOADEDMODULE = 973, // insp-specific - ERR_CANTLOADMODULE = 974, // insp-specific - RPL_LOADEDMODULE = 975 // insp-specific -}; + ERR_BADCHANNELKEY = 475, + ERR_BADCHANMASK = 476, + ERR_INVITEONLYCHAN = 473, + ERR_CHANNELISFULL = 471, + ERR_BANNEDFROMCHAN = 474, + + ERR_BANLISTFULL = 478, + + ERR_NOPRIVILEGES = 481, // rfc, beware though, we use this for other things opers may not do also + ERR_CHANOPRIVSNEEDED = 482, // rfc, beware though, we use this for other things like trying to kick a uline -#endif + ERR_RESTRICTED = 484, + + ERR_NOOPERHOST = 491, + ERR_UNKNOWNSNOMASK = 501, // insp-specific + ERR_USERSDONTMATCH = 502, + ERR_CANTSENDTOUSER = 531, // ??? + + RPL_SYNTAX = 650, // insp-specific + ERR_INVALIDMODEPARAM = 696, // insp-specific + + RPL_OTHERUMODEIS = 803, // insp-specific + RPL_OTHERSNOMASKIS = 804, // insp-specific + + ERR_CANTUNLOADMODULE = 972, // insp-specific + RPL_UNLOADEDMODULE = 973, // insp-specific + ERR_CANTLOADMODULE = 974, // insp-specific + RPL_LOADEDMODULE = 975 // insp-specific +}; diff --git a/include/parammode.h b/include/parammode.h new file mode 100644 index 000000000..b00082bd6 --- /dev/null +++ b/include/parammode.h @@ -0,0 +1,75 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com> + * + * 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/>. + */ + + +#pragma once + +class CoreExport ParamModeBase : public ModeHandler +{ + private: + virtual void OnUnsetInternal(User* source, Channel* chan) = 0; + + public: + ParamModeBase(Module* Creator, const std::string& Name, char modeletter, ParamSpec ps) + : ModeHandler(Creator, Name, modeletter, ps, MODETYPE_CHANNEL, MC_PARAM) { } + + ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& param, bool adding) CXX11_OVERRIDE; + + // Does nothing by default + void OnUnset(User* source, Channel* chan) { } + virtual ModeAction OnSet(User* source, Channel* chan, std::string& param) = 0; + virtual void GetParameter(Channel* chan, std::string& out) = 0; +}; + +/** Defines a parameter mode + * T = Child class + * ExtItemT = Type of the extension item used to store the parameter + * + * When unsetting the mode, the extension is automatically unset. + */ +template <typename T, typename ExtItemT> +class ParamMode : public ParamModeBase +{ + public: + ExtItemT ext; + + /** + * @param Creator Module handling this mode + * @param Name The internal name of this mode + * @param modeletter The mode letter of this mode + * @param ps The parameter type of this mode, one of ParamSpec + */ + ParamMode(Module* Creator, const std::string& Name, char modeletter, ParamSpec ps = PARAM_SETONLY) + : ParamModeBase(Creator, Name, modeletter, ps) + , ext("parammode_" + Name, ExtensionItem::EXT_CHANNEL, Creator) + { + } + + void OnUnsetInternal(User* source, Channel* chan) CXX11_OVERRIDE + { + T* mh = static_cast<T*>(this); + mh->OnUnset(source, chan); + ext.unset(chan); + } + + void GetParameter(Channel* chan, std::string& out) CXX11_OVERRIDE + { + T* mh = static_cast<T*>(this); + mh->SerializeParam(chan, ext.get(chan), out); + } +}; diff --git a/include/protocol.h b/include/protocol.h index aabb5b022..a7c727e8c 100644 --- a/include/protocol.h +++ b/include/protocol.h @@ -18,123 +18,127 @@ */ -#ifndef PROTOCOL_H -#define PROTOCOL_H +#pragma once #include "hashcomp.h" class User; -typedef std::vector<std::string> parameterlist; - -class ProtoServer +class ProtocolServer { public: - std::string servername; - std::string parentname; - std::string gecos; - unsigned int usercount; - unsigned int opercount; - unsigned int latencyms; + /** Send metadata related to this server to the target server + * @param key The 'key' of the data + * @param data The string representation of the data + */ + virtual void SendMetaData(const std::string& key, const std::string& data) = 0; }; -typedef std::list<ProtoServer> ProtoServerList; - -class ProtocolInterface +class CoreExport ProtocolInterface { public: - ProtocolInterface() { } + typedef ProtocolServer Server; + + class ServerInfo + { + public: + std::string servername; + std::string parentname; + std::string description; + unsigned int usercount; + unsigned int opercount; + unsigned int latencyms; + }; + + typedef std::vector<ServerInfo> ServerList; + virtual ~ProtocolInterface() { } - /** Send an ENCAP message to one or more linked servers. + /** Send an ENCAP message to all servers matching a wildcard string. * See the protocol documentation for the purpose of ENCAP. - * @param encap This is a list of string parameters, the first of which must be a server ID or glob matching servernames. - * The second must be a subcommand. All subsequent parameters are dependant on the subcommand. + * @param targetmask The target server mask (can contain wildcards) + * @param cmd The ENCAP subcommand + * @param params List of string parameters which are dependant on the subcommand + * @param source The source of the message (prefix), must be a local user or NULL which means use local server + * @return Always true if the target mask contains wildcards; otherwise true if the server name was found, + * and the message was sent, false if it was not found. * ENCAP (should) be used instead of creating new protocol messages for easier third party application support. - * @return True if the message was sent out (target exists) */ - virtual bool SendEncapsulatedData(const parameterlist &encap) { return false; } + virtual bool SendEncapsulatedData(const std::string& targetmask, const std::string& cmd, const CommandBase::Params& params, User* source = NULL) { return false; } + + /** Send an ENCAP message to all servers. + * See the protocol documentation for the purpose of ENCAP. + * @param cmd The ENCAP subcommand + * @param params List of string parameters which are dependant on the subcommand + * @param source The source of the message (prefix), must be a local user or a user behind 'omit' + * or NULL which is equivalent to the local server + * @param omit If non-NULL the message won't be sent in the direction of this server, useful for forwarding messages + */ + virtual void BroadcastEncap(const std::string& cmd, const CommandBase::Params& params, User* source = NULL, User* omit = NULL) { } - /** Send metadata for an object to other linked servers. - * @param target The object to send metadata for. + /** Send metadata for a channel to other linked servers. + * @param chan The channel to send metadata for * @param key The 'key' of the data, e.g. "swhois" for swhois desc on a user * @param data The string representation of the data */ - virtual void SendMetaData(Extensible* target, const std::string &key, const std::string &data) { } + virtual void SendMetaData(Channel* chan, const std::string& key, const std::string& data) { } - /** Send a topic change for a channel - * @param channel The channel to change the topic for. - * @param topic The new topic to use for the channel. + /** Send metadata for a user to other linked servers. + * @param user The user to send metadata for + * @param key The 'key' of the data, e.g. "swhois" for swhois desc on a user + * @param data The string representation of the data */ - virtual void SendTopic(Channel* channel, std::string &topic) { } + virtual void SendMetaData(User* user, const std::string& key, const std::string& data) { } - /** Send mode changes for an object. - * @param target The channel name or user to send mode changes for. - * @param modedata The mode changes to send. - * @param translate A list of translation types + /** Send metadata related to the server to other linked servers. + * @param key The 'key' of the data + * @param data The string representation of the data */ - virtual void SendMode(const std::string &target, const parameterlist &modedata, const std::vector<TranslateType> &translate) { } - - /** Convenience function, string wrapper around the above. - */ - virtual void SendModeStr(const std::string &target, const std::string &modeline) - { - irc::spacesepstream x(modeline); - parameterlist n; - std::vector<TranslateType> types; - std::string v; - while (x.GetToken(v)) - { - n.push_back(v); - types.push_back(TR_TEXT); - } - SendMode(target, n, types); - } + virtual void SendMetaData(const std::string& key, const std::string& data) { } /** Send a notice to users with a given snomask. * @param snomask The snomask required for the message to be sent. * @param text The message to send. */ - virtual void SendSNONotice(const std::string &snomask, const std::string &text) { } - - /** Send raw data to a remote client. - * @param target The user to push data to. - * @param rawline The raw IRC protocol line to deliver (":me NOTICE you :foo", whatever). - */ - virtual void PushToClient(User* target, const std::string &rawline) { } + virtual void SendSNONotice(char snomask, const std::string& text) { } /** Send a message to a channel. * @param target The channel to message. * @param status The status character (e.g. %) required to recieve. * @param text The message to send. + * @param type The message type (MSG_PRIVMSG or MSG_NOTICE) */ - virtual void SendChannelPrivmsg(Channel* target, char status, const std::string &text) { } + virtual void SendMessage(Channel* target, char status, const std::string& text, MessageType type = MSG_PRIVMSG) { } - /** Send a notice to a channel. - * @param target The channel to message. - * @param status The status character (e.g. %) required to recieve. + /** Send a message to a user. + * @param target The user to message. * @param text The message to send. + * @param type The message type (MSG_PRIVMSG or MSG_NOTICE) */ - virtual void SendChannelNotice(Channel* target, char status, const std::string &text) { } + virtual void SendMessage(User* target, const std::string& text, MessageType type = MSG_PRIVMSG) { } - /** Send a message to a user. - * @param target The user to message. + /** Send a notice to a channel. + * @param target The channel to message. + * @param status The status character (e.g. %) required to recieve. * @param text The message to send. */ - virtual void SendUserPrivmsg(User* target, const std::string &text) { } + void SendChannelNotice(Channel* target, char status, const std::string &text) + { + SendMessage(target, status, text, MSG_NOTICE); + } /** Send a notice to a user. * @param target The user to message. * @param text The message to send. */ - virtual void SendUserNotice(User* target, const std::string &text) { } + void SendUserNotice(User* target, const std::string &text) + { + SendMessage(target, text, MSG_NOTICE); + } /** Fill a list of servers and information about them. * @param sl The list of servers to fill. * XXX: document me properly, this is shit. */ - virtual void GetServerList(ProtoServerList &sl) { } + virtual void GetServerList(ServerList& sl) { } }; - -#endif - diff --git a/include/server.h b/include/server.h new file mode 100644 index 000000000..d73c9673a --- /dev/null +++ b/include/server.h @@ -0,0 +1,73 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com> + * + * 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/>. + */ + + +#pragma once + +class CoreExport Server : public classbase +{ + protected: + /** The name of this server + */ + const std::string name; + + /** The description of this server. + * This can be updated by the protocol module (for remote servers) or by a rehash (for the local server). + */ + std::string description; + + /** True if this server is ulined + */ + bool uline; + + /** True if this server is a silent uline, i.e. silent="true" in the uline block + */ + bool silentuline; + + /** Allow ConfigReaderThread to update the description on a rehash + */ + friend class ConfigReaderThread; + + public: + Server(const std::string& srvname, const std::string& srvdesc) + : name(srvname), description(srvdesc), uline(false), silentuline(false) { } + + /** + * Returns the name of this server + * @return The name of this server, for example "irc.inspircd.org". + */ + const std::string& GetName() const { return name; } + + /** Returns the description of this server + * @return The description of this server + */ + const std::string& GetDesc() const { return description; } + + /** + * Checks whether this server is ulined + * @return True if this server is ulined, false otherwise. + */ + bool IsULine() const { return uline; } + + /** + * Checks whether this server is a silent uline + * Silent uline servers introduce, quit and oper up users without a snotice being generated. + * @return True if this server is a silent uline, false otherwise. + */ + bool IsSilentULine() const { return silentuline; } +}; diff --git a/include/snomasks.h b/include/snomasks.h index 85ad26f71..817371613 100644 --- a/include/snomasks.h +++ b/include/snomasks.h @@ -20,42 +20,63 @@ */ -#ifndef SNOMASKS_H -#define SNOMASKS_H +#pragma once +class SnomaskManager; class Snomask { - public: + /** Description of this snomask, e.g.: OPER, ANNOUNCEMENT, XLINE + */ std::string Description; + + /** Information about the last sent message, + * used for sending "last message repeated X times" messages + */ std::string LastMessage; - int Count; - bool LastBlocked; char LastLetter; + unsigned int Count; + /** Log and send a message to all opers who have the given snomask set + * @param letter The target users of this message + * @param desc The description of this snomask, will be prepended to the message + * @param msg The message to send + */ + static void Send(char letter, const std::string& desc, const std::string& msg); + + public: /** Create a new Snomask */ - Snomask() : Count(0), LastBlocked(false), LastLetter(0) - { - } + Snomask(); /** Sends a message to all opers with this snomask. + * @param message The message to send + * @param letter The snomask character to send the message to. */ - void SendMessage(const std::string &message, char letter); + void SendMessage(const std::string& message, char letter); /** Sends out the (last message repeated N times) message */ void Flush(); + + /** Returns the description of this snomask + * @param letter The letter of this snomask. If uppercase, the description of the remote + * variant of this snomask will be returned (i.e.: "REMOTE" will be prepended to the description). + * @return The description of this snomask + */ + std::string GetDescription(char letter) const; + + friend class SnomaskManager; }; /** Snomask manager handles routing of SNOMASK (usermode +s) messages to opers. * Modules and the core can enable and disable snomask characters. If they do, * then sending snomasks using these characters becomes possible. */ -class CoreExport SnomaskManager +class CoreExport SnomaskManager : public fakederef<SnomaskManager> { - public: Snomask masks[26]; + public: /** Create a new SnomaskManager */ SnomaskManager(); @@ -95,7 +116,6 @@ class CoreExport SnomaskManager */ void WriteGlobalSno(char letter, const char* text, ...) CUSTOM_PRINTF(3, 4); - /** Called once per 5 seconds from the mainloop, this flushes any cached * snotices. The way the caching works is as follows: * Calls to WriteToSnoMask write to a cache, if the call is the same as it was @@ -105,6 +125,12 @@ class CoreExport SnomaskManager * is not particularly significant, in order to keep notices going out. */ void FlushSnotices(); -}; -#endif + /** Check whether a given character is an enabled (initialized) snomask. + * Valid snomask chars are lower- or uppercase letters and have a description. + * Snomasks are initialized with EnableSnomask(). + * @param ch The character to check + * @return True if the given char is allowed to be set via +s. + */ + bool IsSnomaskUsable(char ch) const; +}; diff --git a/include/socket.h b/include/socket.h index 5f6705124..47e89070f 100644 --- a/include/socket.h +++ b/include/socket.h @@ -22,8 +22,7 @@ */ -#ifndef INSPIRCD_SOCKET_H -#define INSPIRCD_SOCKET_H +#pragma once #ifndef _WIN32 @@ -33,6 +32,7 @@ #include <sys/types.h> #include <sys/socket.h> #include <sys/stat.h> +#include <sys/un.h> #include <netinet/in.h> #include <unistd.h> #include <fcntl.h> @@ -60,8 +60,11 @@ namespace irc struct sockaddr sa; struct sockaddr_in in4; struct sockaddr_in6 in6; + struct sockaddr_un un; + /** Return the family of the socket (e.g. AF_INET). */ + int family() const; /** Return the size of the structure for syscall passing */ - int sa_size() const; + socklen_t sa_size() const; /** Return port number or -1 if invalid */ int port() const; /** Return IP only */ @@ -85,7 +88,7 @@ namespace irc /** Construct a CIDR mask from the string. Will normalize (127.0.0.1/8 => 127.0.0.0/8). */ cidr_mask(const std::string& mask); /** Construct a CIDR mask of a given length from the given address */ - cidr_mask(const irc::sockets::sockaddrs& addr, int len); + cidr_mask(const irc::sockets::sockaddrs& addr, unsigned char len); /** Equality of bits, type, and length */ bool operator==(const cidr_mask& other) const; /** Ordering defined for maps */ @@ -110,9 +113,6 @@ namespace irc */ CoreExport bool MatchCIDR(const std::string &address, const std::string &cidr_mask, bool match_with_username); - /** Return the size of the structure for syscall passing */ - inline int sa_size(const irc::sockets::sockaddrs& sa) { return sa.sa_size(); } - /** Convert an address-port pair into a binary sockaddr * @param addr The IP address, IPv4 or IPv6 * @param port The port, 0 for unspecified @@ -120,24 +120,12 @@ namespace irc * @return true if the conversion was successful, false if not. */ CoreExport bool aptosa(const std::string& addr, int port, irc::sockets::sockaddrs& sa); - - /** Convert a binary sockaddr to an address-port pair - * @param sa The structure to convert - * @param addr the IP address - * @param port the port - * @return true if the conversion was successful, false if unknown address family - */ - CoreExport bool satoap(const irc::sockets::sockaddrs& sa, std::string& addr, int &port); - - /** Convert a binary sockaddr to a user-readable string. - * This means IPv6 addresses are written as [::1]:6667, and *:6668 is used for 0.0.0.0:6668 - * @param sa The structure to convert - * @return The string; "<unknown>" if not a valid address - */ - inline std::string satouser(const irc::sockets::sockaddrs& sa) { return sa.str(); } } } +/** A list of failed port bindings, used for informational purposes on startup */ +typedef std::vector<std::pair<irc::sockets::sockaddrs, int> > FailedPortList; + #include "socketengine.h" /** This class handles incoming connections on client ports. * It will create a new User for every valid connection @@ -147,24 +135,37 @@ class CoreExport ListenSocket : public EventHandler { public: reference<ConfigTag> bind_tag; - std::string bind_addr; - int bind_port; - /** Human-readable bind description */ - std::string bind_desc; + const irc::sockets::sockaddrs bind_sa; + + class IOHookProvRef : public dynamic_reference_nocheck<IOHookProvider> + { + public: + IOHookProvRef() + : dynamic_reference_nocheck<IOHookProvider>(NULL, std::string()) + { + } + }; + + typedef TR1NS::array<IOHookProvRef, 2> IOHookProvList; + + /** IOHook providers for handling connections on this socket, + * may be empty. + */ + IOHookProvList iohookprovs; + /** Create a new listening socket */ ListenSocket(ConfigTag* tag, const irc::sockets::sockaddrs& bind_to); - /** Handle an I/O event - */ - void HandleEvent(EventType et, int errornum = 0); /** Close the socket */ ~ListenSocket(); - /** Handles sockets internals crap of a connection, convenience wrapper really + /** Handles new connections, called by the socket engine */ - void AcceptInternal(); -}; - -#endif + void OnEventHandlerRead() CXX11_OVERRIDE; + /** Inspects the bind block belonging to this socket to set the name of the IO hook + * provider which this socket will use for incoming connections. + */ + void ResetIOHookProvider(); +}; diff --git a/include/socketengine.h b/include/socketengine.h index 37b7d6373..01afb8f91 100644 --- a/include/socketengine.h +++ b/include/socketengine.h @@ -20,32 +20,22 @@ */ -#ifndef SOCKETENGINE_H -#define SOCKETENGINE_H +#pragma once #include <vector> #include <string> #include <map> -#include "inspircd_config.h" +#include "config.h" #include "socket.h" #include "base.h" -/** Types of event an EventHandler may receive. - * EVENT_READ is a readable file descriptor, - * and EVENT_WRITE is a writeable file descriptor. - * EVENT_ERROR can always occur, and indicates - * a write error or read error on the socket, - * e.g. EOF condition or broken pipe. - */ -enum EventType -{ - /** Read event */ - EVENT_READ = 0, - /** Write event */ - EVENT_WRITE = 1, - /** Error event */ - EVENT_ERROR = 2 -}; +#ifndef _WIN32 +#include <sys/uio.h> +#endif + +#ifndef IOV_MAX +#define IOV_MAX 1024 +#endif /** * Event mask for SocketEngine events @@ -94,7 +84,7 @@ enum EventMask * EINPROGRESS. An event MAY be sent at any time that writes will not * block. * - * Before calling HandleEvent, a socket engine MAY change the state of + * Before calling OnEventHandler*(), a socket engine MAY change the state of * the FD back to FD_WANT_EDGE_WRITE if it is simpler (for example, if a * one-shot notification was registered). If further writes are needed, * it is the responsibility of the event handler to change the state to @@ -108,7 +98,7 @@ enum EventMask */ FD_WANT_EDGE_WRITE = 0x80, /** Request a one-shot poll-style write notification. The socket will - * return to the FD_WANT_NO_WRITE state before HandleEvent is called. + * return to the FD_WANT_NO_WRITE state before OnEventHandler*() is called. */ FD_WANT_SINGLE_WRITE = 0x100, @@ -116,28 +106,28 @@ enum EventMask FD_WANT_WRITE_MASK = 0x1F0, /** Add a trial read. During the next DispatchEvents invocation, this - * will call HandleEvent with EVENT_READ unless reads are known to be + * will call OnEventHandlerRead() unless reads are known to be * blocking. */ FD_ADD_TRIAL_READ = 0x1000, /** Assert that reads are known to block. This cancels FD_ADD_TRIAL_READ. - * Reset by SE before running EVENT_READ + * Reset by SE before running OnEventHandlerRead(). */ FD_READ_WILL_BLOCK = 0x2000, /** Add a trial write. During the next DispatchEvents invocation, this - * will call HandleEvent with EVENT_WRITE unless writes are known to be + * will call OnEventHandlerWrite() unless writes are known to be * blocking. - * + * * This could be used to group several writes together into a single * send() syscall, or to ensure that writes are blocking when attempting * to use FD_WANT_FAST_WRITE. */ FD_ADD_TRIAL_WRITE = 0x4000, /** Assert that writes are known to block. This cancels FD_ADD_TRIAL_WRITE. - * Reset by SE before running EVENT_WRITE + * Reset by SE before running OnEventHandlerWrite(). */ - FD_WRITE_WILL_BLOCK = 0x8000, + FD_WRITE_WILL_BLOCK = 0x8000, /** Mask for trial read/trial write */ FD_TRIAL_NOTE_MASK = 0x5000 @@ -146,17 +136,14 @@ enum EventMask /** This class is a basic I/O handler class. * Any object which wishes to receive basic I/O events * from the socketengine must derive from this class and - * implement the HandleEvent() method. The derived class + * implement the OnEventHandler*() methods. The derived class * must then be added to SocketEngine using the method * SocketEngine::AddFd(), after which point the derived - * class will receive events to its HandleEvent() method. - * The derived class should also implement one of Readable() - * and Writeable(). In the current implementation, only - * Readable() is used. If this returns true, the socketengine - * inserts a readable socket. If it is false, the socketengine - * inserts a writeable socket. The derived class should never - * change the value this function returns without first - * deleting the socket from the socket engine. The only + * class will receive events to its OnEventHandler*() methods. + * The event mask passed to SocketEngine::AddFd() determines + * what events the EventHandler gets notified about and with + * what semantics. SocketEngine::ChangeEventMask() can be + * called to update the event mask later. The only * requirement beyond this for an event handler is that it * must have a file descriptor. What this file descriptor * is actually attached to is completely up to you. @@ -166,6 +153,9 @@ class CoreExport EventHandler : public classbase private: /** Private state maintained by socket engine */ int event_mask; + + void SetEventMask(int mask) { event_mask = mask; } + protected: /** File descriptor. * All events which can be handled must have a file descriptor. This @@ -197,16 +187,20 @@ class CoreExport EventHandler : public classbase */ virtual ~EventHandler() {} - /** Process an I/O event. - * You MUST implement this function in your derived - * class, and it will be called whenever read or write - * events are received. - * @param et either one of EVENT_READ for read events, - * EVENT_WRITE for write events and EVENT_ERROR for - * error events. - * @param errornum The error code which goes with an EVENT_ERROR. + /** Called by the socket engine in case of a read event + */ + virtual void OnEventHandlerRead() = 0; + + /** Called by the socket engine in case of a write event. + * The default implementation does nothing. */ - virtual void HandleEvent(EventType et, int errornum = 0) = 0; + virtual void OnEventHandlerWrite(); + + /** Called by the socket engine in case of an error event. + * The default implementation does nothing. + * @param errornum Error code + */ + virtual void OnEventHandlerError(int errornum); friend class SocketEngine; }; @@ -217,47 +211,102 @@ class CoreExport EventHandler : public classbase * its private members and internal behaviour * should be treated as blackboxed, and vary * from system to system and upon the config - * settings chosen by the server admin. The current - * version supports select, epoll and kqueue. - * The configure script will enable a socket engine - * based upon what OS is detected, and will derive - * a class from SocketEngine based upon what it finds. - * The derived classes file will also implement a - * classfactory, SocketEngineFactory, which will - * create a derived instance of SocketEngine using - * polymorphism so that the core and modules do not - * have to be aware of which SocketEngine derived - * class they are using. + * settings chosen by the server admin. */ class CoreExport SocketEngine { - protected: - /** Current number of descriptors in the engine - */ - int CurrentSetSize; + public: + /** Socket engine statistics: count of various events, bandwidth usage + */ + class Statistics + { + mutable size_t indata; + mutable size_t outdata; + mutable time_t lastempty; + + /** Reset the byte counters and lastempty if there wasn't a reset in this second. + */ + void CheckFlush() const; + + public: + /** Constructor, initializes member vars except indata and outdata because those are set to 0 + * in CheckFlush() the first time Update() or GetBandwidth() is called. + */ + Statistics() : lastempty(0), TotalEvents(0), ReadEvents(0), WriteEvents(0), ErrorEvents(0) { } + + /** Update counters for network data received. + * This should be called after every read-type syscall. + * @param len_in Number of bytes received, or -1 for error, as typically + * returned by a read-style syscall. + */ + void UpdateReadCounters(int len_in); + + /** Update counters for network data sent. + * This should be called after every write-type syscall. + * @param len_out Number of bytes sent, or -1 for error, as typically + * returned by a read-style syscall. + */ + void UpdateWriteCounters(int len_out); + + /** Get data transfer statistics. + * @param kbitpersec_in Filled with incoming traffic in this second in kbit/s. + * @param kbitpersec_out Filled with outgoing traffic in this second in kbit/s. + * @param kbitpersec_total Filled with total traffic in this second in kbit/s. + */ + void CoreExport GetBandwidth(float& kbitpersec_in, float& kbitpersec_out, float& kbitpersec_total) const; + + unsigned long TotalEvents; + unsigned long ReadEvents; + unsigned long WriteEvents; + unsigned long ErrorEvents; + }; + + private: /** Reference table, contains all current handlers - */ - EventHandler** ref; + **/ + static std::vector<EventHandler*> ref; + + /** Current number of descriptors in the engine. */ + static size_t CurrentSetSize; + + /** The maximum number of descriptors in the engine. */ + static size_t MaxSetSize; + /** List of handlers that want a trial read/write */ - std::set<int> trials; + static std::set<int> trials; - int MAX_DESCRIPTORS; + /** Socket engine statistics: count of various events, bandwidth usage + */ + static Statistics stats; - size_t indata; - size_t outdata; - time_t lastempty; + /** Look up the fd limit using rlimit. */ + static void LookupMaxFds(); - void UpdateStats(size_t len_in, size_t len_out); + /** Terminates the program when the socket engine fails to initialize. */ + static void InitError(); - virtual void OnSetEvent(EventHandler* eh, int old_mask, int new_mask) = 0; - void SetEventMask(EventHandler* eh, int value); -public: + static void OnSetEvent(EventHandler* eh, int old_mask, int new_mask); + + /** Add an event handler to the base socket engine. AddFd(EventHandler*, int) should call this. + */ + static bool AddFdRef(EventHandler* eh); + + static void DelFdRef(EventHandler* eh); - unsigned long TotalEvents; - unsigned long ReadEvents; - unsigned long WriteEvents; - unsigned long ErrorEvents; + template <typename T> + static void ResizeDouble(std::vector<T>& vect) + { + if (SocketEngine::CurrentSetSize > vect.size()) + vect.resize(vect.size() * 2); + } + +public: +#ifndef _WIN32 + typedef iovec IOVector; +#else + typedef WindowsIOVec IOVector; +#endif /** Constructor. * The constructor transparently initializes @@ -266,23 +315,25 @@ public: * failure (for example, you try and enable * epoll on a 2.4 linux kernel) then this * function may bail back to the shell. + * @return void, but it is acceptable for this function to bail back to + * the shell or operating system on fatal error. */ - SocketEngine(); + static void Init(); /** Destructor. * The destructor transparently tidies up * any resources used by the socket engine. */ - virtual ~SocketEngine(); + static void Deinit(); /** Add an EventHandler object to the engine. Use AddFd to add a file * descriptor to the engine and have the socket engine monitor it. You * must provide an object derived from EventHandler which implements - * HandleEvent(). + * the required OnEventHandler*() methods. * @param eh An event handling object to add * @param event_mask The initial event mask for the object */ - virtual bool AddFd(EventHandler* eh, int event_mask) = 0; + static bool AddFd(EventHandler* eh, int event_mask); /** If you call this function and pass it an * event handler, that event handler will @@ -295,17 +346,19 @@ public: * @param eh The event handler to change * @param event_mask The changes to make to the wait state */ - void ChangeEventMask(EventHandler* eh, int event_mask); + static void ChangeEventMask(EventHandler* eh, int event_mask); - /** Returns the highest file descriptor you may store in the socket engine - * @return The maximum fd value + /** Returns the number of file descriptors reported by the system this program may use + * when it was started. + * @return If non-zero the number of file descriptors that the system reported that we + * may use. */ - inline int GetMaxFds() const { return MAX_DESCRIPTORS; } + static size_t GetMaxFds() { return MaxSetSize; } /** Returns the number of file descriptors being queried * @return The set size */ - inline int GetUsedFds() const { return CurrentSetSize; } + static size_t GetUsedFds() { return CurrentSetSize; } /** Delete an event handler from the engine. * This function call deletes an EventHandler @@ -315,47 +368,40 @@ public: * required you must do this yourself. * @param eh The event handler object to remove */ - virtual void DelFd(EventHandler* eh) = 0; + static void DelFd(EventHandler* eh); /** Returns true if a file descriptor exists in * the socket engine's list. * @param fd The event handler to look for * @return True if this fd has an event handler */ - virtual bool HasFd(int fd); + static bool HasFd(int fd); /** Returns the EventHandler attached to a specific fd. * If the fd isnt in the socketengine, returns NULL. * @param fd The event handler to look for * @return A pointer to the event handler, or NULL */ - virtual EventHandler* GetRef(int fd); + static EventHandler* GetRef(int fd); /** Waits for events and dispatches them to handlers. Please note that * this doesn't wait long, only a couple of milliseconds. It returns the * number of events which occurred during this call. This method will * dispatch events to their handlers by calling their - * EventHandler::HandleEvent() methods with the necessary EventType - * value. + * EventHandler::OnEventHandler*() methods. * @return The number of events which have occured. */ - virtual int DispatchEvents() = 0; + static int DispatchEvents(); /** Dispatch trial reads and writes. This causes the actual socket I/O * to happen when writes have been pre-buffered. */ - virtual void DispatchTrialWrites(); - - /** Returns the socket engines name. This returns the name of the - * engine for use in /VERSION responses. - * @return The socket engine name - */ - virtual std::string GetName() = 0; + static void DispatchTrialWrites(); /** Returns true if the file descriptors in the given event handler are * within sensible ranges which can be handled by the socket engine. */ - virtual bool BoundsCheckFd(EventHandler* eh); + static bool BoundsCheckFd(EventHandler* eh); /** Abstraction for BSD sockets accept(2). * This function should emulate its namesake system call exactly. @@ -364,21 +410,20 @@ public: * @param addrlen The size of the sockaddr parameter. * @return This method should return exactly the same values as the system call it emulates. */ - int Accept(EventHandler* fd, sockaddr *addr, socklen_t *addrlen); + static int Accept(EventHandler* fd, sockaddr *addr, socklen_t *addrlen); - /** Abstraction for BSD sockets close(2). - * This function should emulate its namesake system call exactly. - * @param fd This version of the call takes an EventHandler instead of a bare file descriptor. - * @return This method should return exactly the same values as the system call it emulates. + /** Close the underlying fd of an event handler, remove it from the socket engine and set the fd to -1. + * @param eh The EventHandler to close. + * @return 0 on success, a negative value on error */ - int Close(EventHandler* fd); + static int Close(EventHandler* eh); /** Abstraction for BSD sockets close(2). * This function should emulate its namesake system call exactly. * This function should emulate its namesake system call exactly. * @return This method should return exactly the same values as the system call it emulates. */ - int Close(int fd); + static int Close(int fd); /** Abstraction for BSD sockets send(2). * This function should emulate its namesake system call exactly. @@ -388,7 +433,28 @@ public: * @param flags A flag value that controls the sending of the data. * @return This method should return exactly the same values as the system call it emulates. */ - int Send(EventHandler* fd, const void *buf, size_t len, int flags); + static int Send(EventHandler* fd, const void *buf, size_t len, int flags); + + /** Abstraction for vector write function writev(). + * This function should emulate its namesake system call exactly. + * @param fd EventHandler to send data with + * @param iov Array of IOVectors containing the buffers to send and their lengths in the platform's + * native format. + * @param count Number of elements in iov. + * @return This method should return exactly the same values as the system call it emulates. + */ + static int WriteV(EventHandler* fd, const IOVector* iov, int count); + +#ifdef _WIN32 + /** Abstraction for vector write function writev() that accepts a POSIX format iovec. + * This function should emulate its namesake system call exactly. + * @param fd EventHandler to send data with + * @param iov Array of iovecs containing the buffers to send and their lengths in POSIX format. + * @param count Number of elements in iov. + * @return This method should return exactly the same values as the system call it emulates. + */ + static int WriteV(EventHandler* fd, const iovec* iov, int count); +#endif /** Abstraction for BSD sockets recv(2). * This function should emulate its namesake system call exactly. @@ -398,7 +464,7 @@ public: * @param flags A flag value that controls the reception of the data. * @return This method should return exactly the same values as the system call it emulates. */ - int Recv(EventHandler* fd, void *buf, size_t len, int flags); + static int Recv(EventHandler* fd, void *buf, size_t len, int flags); /** Abstraction for BSD sockets recvfrom(2). * This function should emulate its namesake system call exactly. @@ -410,7 +476,7 @@ public: * @param fromlen The size of the from parameter. * @return This method should return exactly the same values as the system call it emulates. */ - int RecvFrom(EventHandler* fd, void *buf, size_t len, int flags, sockaddr *from, socklen_t *fromlen); + static int RecvFrom(EventHandler* fd, void *buf, size_t len, int flags, sockaddr *from, socklen_t *fromlen); /** Abstraction for BSD sockets sendto(2). * This function should emulate its namesake system call exactly. @@ -418,32 +484,30 @@ public: * @param buf The buffer in which the data that is sent is stored. * @param len The size of the buffer. * @param flags A flag value that controls the sending of the data. - * @param to The remote IP address and port. - * @param tolen The size of the to parameter. + * @param address The remote IP address and port. * @return This method should return exactly the same values as the system call it emulates. */ - int SendTo(EventHandler* fd, const void *buf, size_t len, int flags, const sockaddr *to, socklen_t tolen); + static int SendTo(EventHandler* fd, const void* buf, size_t len, int flags, const irc::sockets::sockaddrs& address); /** Abstraction for BSD sockets connect(2). * This function should emulate its namesake system call exactly. * @param fd This version of the call takes an EventHandler instead of a bare file descriptor. - * @param serv_addr The server IP address and port. - * @param addrlen The size of the sockaddr parameter. + * @param address The server IP address and port. * @return This method should return exactly the same values as the system call it emulates. */ - int Connect(EventHandler* fd, const sockaddr *serv_addr, socklen_t addrlen); + static int Connect(EventHandler* fd, const irc::sockets::sockaddrs& address); /** Make a file descriptor blocking. * @param fd a file descriptor to set to blocking mode * @return 0 on success, -1 on failure, errno is set appropriately. */ - int Blocking(int fd); + static int Blocking(int fd); /** Make a file descriptor nonblocking. * @param fd A file descriptor to set to nonblocking mode * @return 0 on success, -1 on failure, errno is set appropriately. */ - int NonBlocking(int fd); + static int NonBlocking(int fd); /** Abstraction for BSD sockets shutdown(2). * This function should emulate its namesake system call exactly. @@ -451,29 +515,29 @@ public: * @param how What part of the socket to shut down * @return This method should return exactly the same values as the system call it emulates. */ - int Shutdown(EventHandler* fd, int how); + static int Shutdown(EventHandler* fd, int how); /** Abstraction for BSD sockets shutdown(2). * This function should emulate its namesake system call exactly. * @return This method should return exactly the same values as the system call it emulates. */ - int Shutdown(int fd, int how); + static int Shutdown(int fd, int how); /** Abstraction for BSD sockets bind(2). * This function should emulate its namesake system call exactly. * @return This method should return exactly the same values as the system call it emulates. */ - int Bind(int fd, const irc::sockets::sockaddrs& addr); + static int Bind(int fd, const irc::sockets::sockaddrs& addr); /** Abstraction for BSD sockets listen(2). * This function should emulate its namesake system call exactly. * @return This method should return exactly the same values as the system call it emulates. */ - int Listen(int sockfd, int backlog); + static int Listen(int sockfd, int backlog); /** Set SO_REUSEADDR and SO_LINGER on this file descriptor */ - void SetReuse(int sockfd); + static void SetReuse(int sockfd); /** This function is called immediately after fork(). * Some socket engines (notably kqueue) cannot have their @@ -484,11 +548,11 @@ public: * @return void, but it is acceptable for this function to bail back to * the shell or operating system on fatal error. */ - virtual void RecoverFromFork(); + static void RecoverFromFork(); - /** Get data transfer statistics, kilobits per second in and out and total. + /** Get data transfer and event statistics */ - void GetStats(float &kbitpersec_in, float &kbitpersec_out, float &kbitpersec_total); + static const Statistics& GetStats() { return stats; } /** Should we ignore the error in errno? * Checks EAGAIN and WSAEWOULDBLOCK @@ -516,8 +580,3 @@ inline bool SocketEngine::IgnoreError() return false; } - -SocketEngine* CreateSocketEngine(); - -#endif - diff --git a/include/stdalgo.h b/include/stdalgo.h new file mode 100644 index 000000000..d69f50bb2 --- /dev/null +++ b/include/stdalgo.h @@ -0,0 +1,295 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com> + * + * 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/>. + */ + + +#pragma once + +namespace stdalgo +{ + namespace vector + { + /** + * Erase a single element from a vector by overwriting it with a copy of the last element, + * which is then removed. This, in contrast to vector::erase(), does not result in all + * elements after the erased element being moved. + * @param vect Vector to remove the element from + * @param it Iterator to the element to remove + * @return Nothing, but all iterators, references and pointers to the erased element and the + * last element are invalidated + */ + template <typename T> + inline void swaperase(typename std::vector<T>& vect, const typename std::vector<T>::iterator& it) + { + *it = vect.back(); + vect.pop_back(); + } + + /** + * Find and if exists, erase a single element from a vector by overwriting it with a + * copy of the last element, which is then removed. This, in contrast to vector::erase(), + * does not result in all elements after the erased element being moved. + * If the given value occurs multiple times, the one with the lowest index is removed. + * Individual elements are compared to the given value using operator==(). + * @param vect Vector to remove the element from + * @param val Value of the element to look for and remove + * @return True if the element was found and removed, false if it wasn't found. + * If true, all iterators, references and pointers pointing to either the first element that + * is equal to val or to the last element are invalidated. + */ + template <typename T> + inline bool swaperase(typename std::vector<T>& vect, const T& val) + { + const typename std::vector<T>::iterator it = std::find(vect.begin(), vect.end(), val); + if (it != vect.end()) + { + swaperase(vect, it); + return true; + } + return false; + } + } + + namespace string + { + /** Get underlying C string of the string passed as parameter. Useful in template functions. + * @param str C string + * @return Same as input + */ + inline const char* tocstr(const char* str) + { + return str; + } + + /** Get underlying C string of the string passed as parameter. Useful in template functions. + * @param str std::string object + * @return str.c_str() + */ + inline const char* tocstr(const std::string& str) + { + return str.c_str(); + } + + /** Check if two strings are equal case insensitively. + * @param str1 First string to compare. + * @param str2 Second string to compare. + * @return True if the strings are equal case-insensitively, false otherwise. + */ + template <typename S1, typename S2> + inline bool equalsci(const S1& str1, const S2& str2) + { + return (!strcasecmp(tocstr(str1), tocstr(str2))); + } + + /** Joins the contents of a vector to a string. + * @param sequence Zero or more items to join. + * @param separator The character to place between the items, defaults to ' ' (space). + * @return The joined string. + */ + template<typename Collection> + inline std::string join(const Collection& sequence, char separator = ' ') + { + std::string joined; + if (sequence.empty()) + return joined; + + for (typename Collection::const_iterator iter = sequence.begin(); iter != sequence.end(); ++iter) + joined.append(ConvToStr(*iter)).push_back(separator); + + joined.erase(joined.end() - 1); + return joined; + } + + /** Replace first occurrence of a substring ('target') in a string ('str') with another string ('replacement'). + * @param str String to perform replacement in + * @param target String to replace + * @param replacement String to put in place of 'target' + * @return True if 'target' was replaced with 'replacement', false if it was not found in 'str'. + */ + template<typename CharT, typename Traits, typename Alloc> + inline bool replace(std::basic_string<CharT, Traits, Alloc>& str, const std::basic_string<CharT, Traits, Alloc>& target, const std::basic_string<CharT, Traits, Alloc>& replacement) + { + const typename std::basic_string<CharT, Traits, Alloc>::size_type p = str.find(target); + if (p == std::basic_string<CharT, Traits, Alloc>::npos) + return false; + str.replace(p, target.size(), replacement); + return true; + } + + /** Replace all occurrences of a string ('target') in a string ('str') with another string ('replacement'). + * @param str String to perform replacement in + * @param target String to replace + * @param replacement String to put in place of 'target' + */ + template<typename CharT, typename Traits, typename Alloc> + inline void replace_all(std::basic_string<CharT, Traits, Alloc>& str, const std::basic_string<CharT, Traits, Alloc>& target, const std::basic_string<CharT, Traits, Alloc>& replacement) + { + if (target.empty()) + return; + + typename std::basic_string<CharT, Traits, Alloc>::size_type p = 0; + while ((p = str.find(target, p)) != std::basic_string<CharT, Traits, Alloc>::npos) + { + str.replace(p, target.size(), replacement); + p += replacement.size(); + } + } + } + + /** + * Deleter that uses operator delete to delete the item + */ + template <typename T> + struct defaultdeleter + { + void operator()(T* o) + { + delete o; + } + }; + + /** + * Deleter that adds the item to the cull list, that is, queues it for + * deletion at the end of the current mainloop iteration + */ + struct culldeleter + { + void operator()(classbase* item); + }; + + /** + * Deletes all elements in a container using operator delete + * @param cont The container containing the elements to delete + */ + template <template<typename, typename> class Cont, typename T, typename Alloc> + inline void delete_all(const Cont<T*, Alloc>& cont) + { + std::for_each(cont.begin(), cont.end(), defaultdeleter<T>()); + } + + /** + * Remove an element from a container + * @param cont Container to remove the element from + * @param val Value of the element to look for and remove + * @return True if the element was found and removed, false otherwise + */ + template <template<typename, typename> class Cont, typename T, typename Alloc> + inline bool erase(Cont<T, Alloc>& cont, const T& val) + { + const typename Cont<T, Alloc>::iterator it = std::find(cont.begin(), cont.end(), val); + if (it != cont.end()) + { + cont.erase(it); + return true; + } + return false; + } + + /** + * Check if an element with the given value is in a container. Equivalent to (std::find(cont.begin(), cont.end(), val) != cont.end()). + * @param cont Container to find the element in + * @param val Value of the element to look for + * @return True if the element was found in the container, false otherwise + */ + template <template<typename, typename> class Cont, typename T, typename Alloc> + inline bool isin(const Cont<T, Alloc>& cont, const T& val) + { + return (std::find(cont.begin(), cont.end(), val) != cont.end()); + } + + namespace string + { + /** + * Escape a string + * @param str String to escape + * @param out Output, must not be the same string as str + */ + template <char from, char to, char esc> + inline void escape(const std::string& str, std::string& out) + { + for (std::string::const_iterator i = str.begin(); i != str.end(); ++i) + { + char c = *i; + if (c == esc) + out.append(2, esc); + else + { + if (c == from) + { + out.push_back(esc); + c = to; + } + out.push_back(c); + } + } + } + + /** + * Escape a string using the backslash character as the escape character + * @param str String to escape + * @param out Output, must not be the same string as str + */ + template <char from, char to> + inline void escape(const std::string& str, std::string& out) + { + escape<from, to, '\\'>(str, out); + } + + /** + * Unescape a string + * @param str String to unescape + * @param out Output, must not be the same string as str + * @return True if the string was unescaped, false if an invalid escape sequence is present in the input in which case out will contain a partially unescaped string + */ + template<char from, char to, char esc> + inline bool unescape(const std::string& str, std::string& out) + { + for (std::string::const_iterator i = str.begin(); i != str.end(); ++i) + { + char c = *i; + if (c == '\\') + { + ++i; + if (i == str.end()) + return false; + + char nextc = *i; + if (nextc == esc) + c = esc; + else if (nextc != to) + return false; // Invalid escape sequence + else + c = from; + } + out.push_back(c); + } + return true; + } + + /** + * Unescape a string using the backslash character as the escape character + * @param str String to unescape + * @param out Output, must not be the same string as str + * @return True if the string was unescaped, false if an invalid escape sequence is present in the input in which case out will contain a partially unescaped string + */ + template <char from, char to> + inline bool unescape(const std::string& str, std::string& out) + { + return unescape<from, to, '\\'>(str, out); + } + } +} diff --git a/include/testsuite.h b/include/testsuite.h index f91e508c9..c760513f8 100644 --- a/include/testsuite.h +++ b/include/testsuite.h @@ -16,12 +16,12 @@ */ -#ifndef TESTSUITE_H -#define TESTSUITE_H +#pragma once + +#ifdef INSPIRCD_ENABLE_TESTSUITE class TestSuite { - bool RealGenerateUIDTests(); public: TestSuite(); ~TestSuite(); diff --git a/include/threadengine.h b/include/threadengine.h index 4bf5a48f3..964b8d796 100644 --- a/include/threadengine.h +++ b/include/threadengine.h @@ -18,17 +18,14 @@ */ -#ifndef THREADENGINE_H -#define THREADENGINE_H +#pragma once #include <vector> #include <string> #include <map> -#include "inspircd_config.h" +#include "config.h" #include "base.h" -class ThreadData; - /** Derive from this class to implement your own threaded sections of * code. Be sure to keep your code thread-safe and not prone to deadlocks * and race conditions if you MUST use threading! @@ -39,6 +36,15 @@ class CoreExport Thread /** Set to true when the thread is to exit */ bool ExitFlag; + + /** Opaque thread state managed by the ThreadEngine + */ + ThreadEngine::ThreadState state; + + /** ThreadEngine manages Thread::state + */ + friend class ThreadEngine; + protected: /** Get thread's current exit status. * (are we being asked to exit?) @@ -48,19 +54,12 @@ class CoreExport Thread return ExitFlag; } public: - /** Opaque thread state managed by threading engine - */ - ThreadData* state; - /** Set Creator to NULL at this point */ - Thread() : ExitFlag(false), state(NULL) + Thread() : ExitFlag(false) { } - /* If the thread is running, you MUST join BEFORE deletion */ - virtual ~Thread(); - /** Override this method to put your actual * threaded code here. */ @@ -109,7 +108,7 @@ class CoreExport QueuedThread : public Thread queue.Wakeup(); queue.Unlock(); } - virtual void SetExitFlag() + void SetExitFlag() CXX11_OVERRIDE { queue.Lock(); Thread::SetExitFlag(); @@ -158,7 +157,7 @@ class CoreExport SocketThread : public Thread queue.Wakeup(); queue.Unlock(); } - virtual void SetExitFlag() + void SetExitFlag() CXX11_OVERRIDE { queue.Lock(); Thread::SetExitFlag(); @@ -172,6 +171,3 @@ class CoreExport SocketThread : public Thread */ virtual void OnNotify() = 0; }; - -#endif - diff --git a/include/threadengines/threadengine_pthread.h b/include/threadengines/threadengine_pthread.h index 5168ed238..ca3354260 100644 --- a/include/threadengines/threadengine_pthread.h +++ b/include/threadengines/threadengine_pthread.h @@ -18,8 +18,7 @@ */ -#ifndef THREADENGINE_PTHREAD_H -#define THREADENGINE_PTHREAD_H +#pragma once #include <pthread.h> #include "typedefs.h" @@ -37,14 +36,12 @@ class CoreExport ThreadEngine { public: - - /** Constructor. - */ - ThreadEngine(); - - /** Destructor + /** Per-thread state, present in each Thread object, managed by the ThreadEngine */ - virtual ~ThreadEngine(); + struct ThreadState + { + pthread_t pthread_id; + }; /** Create a new thread. This takes an already allocated * Thread* pointer and initializes it to use this threading @@ -54,20 +51,17 @@ class CoreExport ThreadEngine */ void Start(Thread* thread_to_init); - /** Returns the thread engine's name for display purposes - * @return The thread engine name + /** Stop a thread gracefully. + * First, this function asks the thread to terminate by calling Thread::SetExitFlag(). + * Next, it waits until the thread terminates (on the operating system level). Finally, + * all OS-level resources associated with the thread are released. The Thread instance + * passed to the function is NOT freed. + * When this function returns, the thread is stopped and you can destroy it or restart it + * at a later point. + * Stopping a thread that is not running is a bug. + * @param thread The thread to stop. */ - const std::string GetName() - { - return "posix-thread"; - } -}; - -class CoreExport ThreadData -{ - public: - pthread_t pthread_id; - void FreeThread(Thread* toFree); + void Stop(Thread* thread); }; /** The Mutex class represents a mutex, which can be used to keep threads @@ -80,7 +74,7 @@ class CoreExport ThreadData */ class CoreExport Mutex { - private: + protected: pthread_mutex_t putex; public: /** Constructor. @@ -109,33 +103,20 @@ class CoreExport Mutex } }; -class ThreadQueueData +class ThreadQueueData : public Mutex { - pthread_mutex_t mutex; pthread_cond_t cond; public: ThreadQueueData() { - pthread_mutex_init(&mutex, NULL); pthread_cond_init(&cond, NULL); } ~ThreadQueueData() { - pthread_mutex_destroy(&mutex); pthread_cond_destroy(&cond); } - void Lock() - { - pthread_mutex_lock(&mutex); - } - - void Unlock() - { - pthread_mutex_unlock(&mutex); - } - void Wakeup() { pthread_cond_signal(&cond); @@ -143,7 +124,7 @@ class ThreadQueueData void Wait() { - pthread_cond_wait(&cond, &mutex); + pthread_cond_wait(&cond, &putex); } }; @@ -153,6 +134,3 @@ class ThreadSignalData public: ThreadSignalSocket* sock; }; - - -#endif diff --git a/include/threadengines/threadengine_win32.h b/include/threadengines/threadengine_win32.h index f068ac707..aac7b8b97 100644 --- a/include/threadengines/threadengine_win32.h +++ b/include/threadengines/threadengine_win32.h @@ -18,10 +18,9 @@ */ -#ifndef THREADENGINE_WIN32_H -#define THREADENGINE_WIN32_H +#pragma once -#include "inspircd_config.h" +#include "config.h" #include "base.h" class Thread; @@ -39,10 +38,12 @@ class Thread; class CoreExport ThreadEngine { public: - - ThreadEngine(); - - virtual ~ThreadEngine(); + /** Per-thread state, present in each Thread object, managed by the ThreadEngine + */ + struct ThreadState + { + HANDLE handle; + }; static DWORD WINAPI Entry(void* parameter); @@ -54,20 +55,17 @@ class CoreExport ThreadEngine */ void Start(Thread* thread_to_init); - /** Returns the thread engine's name for display purposes - * @return The thread engine name + /** Stop a thread gracefully. + * First, this function asks the thread to terminate by calling Thread::SetExitFlag(). + * Next, it waits until the thread terminates (on the operating system level). Finally, + * all OS-level resources associated with the thread are released. The Thread instance + * passed to the function is NOT freed. + * When this function returns, the thread is stopped and you can destroy it or restart it + * at a later point. + * Stopping a thread that is not running is a bug. + * @param thread The thread to stop. */ - const std::string GetName() - { - return "windows-thread"; - } -}; - -class CoreExport ThreadData -{ - public: - HANDLE handle; - void FreeThread(Thread* toFree); + void Stop(Thread* thread); }; /** The Mutex class represents a mutex, which can be used to keep threads @@ -101,9 +99,8 @@ class CoreExport Mutex } }; -class ThreadQueueData +class ThreadQueueData : public Mutex { - CRITICAL_SECTION mutex; HANDLE event; public: ThreadQueueData() @@ -111,23 +108,11 @@ class ThreadQueueData event = CreateEvent(NULL, false, false, NULL); if (event == NULL) throw CoreException("CreateEvent() failed in ThreadQueueData::ThreadQueueData()!"); - InitializeCriticalSection(&mutex); } ~ThreadQueueData() { CloseHandle(event); - DeleteCriticalSection(&mutex); - } - - void Lock() - { - EnterCriticalSection(&mutex); - } - - void Unlock() - { - LeaveCriticalSection(&mutex); } void Wakeup() @@ -137,9 +122,9 @@ class ThreadQueueData void Wait() { - LeaveCriticalSection(&mutex); + Unlock(); WaitForSingleObject(event, INFINITE); - EnterCriticalSection(&mutex); + Lock(); } }; @@ -152,6 +137,3 @@ class ThreadSignalData connFD = -1; } }; - -#endif - diff --git a/include/timer.h b/include/timer.h index 9bb7128b8..a116d456c 100644 --- a/include/timer.h +++ b/include/timer.h @@ -19,8 +19,9 @@ */ -#ifndef INSPIRCD_TIMER_H -#define INSPIRCD_TIMER_H +#pragma once + +class Module; /** Timer class for one-second resolution timers * Timer provides a facility which allows module @@ -29,61 +30,65 @@ * resolution. To use Timer, inherit a class from * Timer, then insert your inherited class into the * queue using Server::AddTimer(). The Tick() method of - * your object (which you should override) will be called + * your object (which you have to override) will be called * at the given time. */ class CoreExport Timer { - private: /** The triggering time */ time_t trigger; + /** Number of seconds between triggers */ - long secs; + unsigned int secs; + /** True if this is a repeating timer */ bool repeat; + public: /** Default constructor, initializes the triggering time * @param secs_from_now The number of seconds from now to trigger the timer - * @param now The time now * @param repeating Repeat this timer every secs_from_now seconds if set to true */ - Timer(long secs_from_now, time_t now, bool repeating = false) - { - trigger = now + secs_from_now; - secs = secs_from_now; - repeat = repeating; - } + Timer(unsigned int secs_from_now, bool repeating = false); - /** Default destructor, does nothing. + /** Default destructor, removes the timer from the timer manager */ - virtual ~Timer() { } + virtual ~Timer(); /** Retrieve the current triggering time */ - virtual time_t GetTimer() + time_t GetTrigger() const { return trigger; } /** Sets the trigger timeout to a new value + * This does not update the bookkeeping in TimerManager, use SetInterval() + * to change the interval between ticks while keeping TimerManager updated */ - virtual void SetTimer(time_t t) + void SetTrigger(time_t nexttrigger) { - trigger = t; + trigger = nexttrigger; } + /** Sets the interval between two ticks. + */ + void SetInterval(unsigned int interval); + /** Called when the timer ticks. * You should override this method with some useful code to * handle the tick event. + * @param TIME The current time. + * @return True if the Timer object is still valid, false if it was destructed. */ - virtual void Tick(time_t TIME) = 0; + virtual bool Tick(time_t TIME) = 0; /** Returns true if this timer is set to repeat */ - bool GetRepeat() + bool GetRepeat() const { return repeat; } @@ -91,7 +96,7 @@ class CoreExport Timer /** Returns the interval (number of seconds between ticks) * of this timer object. */ - long GetSecs() + unsigned int GetInterval() const { return secs; } @@ -99,12 +104,6 @@ class CoreExport Timer /** Cancels the repeat state of a repeating timer. * If you call this method, then the next time your * timer ticks, it will be removed immediately after. - * You should use this method call to remove a recurring - * timer if you wish to do so within the timer's Tick - * event, as calling TimerManager::DelTimer() from within - * the Timer::Tick() method is dangerous and may - * cause a segmentation fault. Calling CancelRepeat() - * is safe in this case. */ void CancelRepeat() { @@ -112,24 +111,19 @@ class CoreExport Timer } }; - /** This class manages sets of Timers, and triggers them at their defined times. * This will ensure timers are not missed, as well as removing timers that have * expired and allowing the addition of new ones. */ class CoreExport TimerManager { - protected: + typedef std::multimap<time_t, Timer*> TimerMap; + /** A list of all pending timers */ - std::vector<Timer *> Timers; + TimerMap Timers; public: - /** Constructor - */ - TimerManager(); - ~TimerManager(); - /** Tick all pending Timers * @param TIME the current system time */ @@ -140,15 +134,8 @@ class CoreExport TimerManager */ void AddTimer(Timer *T); - /** Delete an Timer - * @param T an Timer derived class to delete + /** Remove a Timer + * @param T an Timer derived class to remove */ void DelTimer(Timer* T); - - /** Compares two timers - */ - static bool TimerComparison( Timer *one, Timer*two); }; - -#endif - diff --git a/include/token_list.h b/include/token_list.h new file mode 100644 index 000000000..ffa3b89e6 --- /dev/null +++ b/include/token_list.h @@ -0,0 +1,65 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2017 Peter Powell <petpow@saberuk.com> + * + * 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/>. + */ + + +#pragma once + +#include "compat.h" + +class CoreExport TokenList +{ + private: + /** Whether this list includes all tokens by default. */ + bool permissive; + + /** Either the tokens to exclude if in permissive mode or the tokens to include if in strict mode. */ + insp::flat_set<std::string, irc::insensitive_swo> tokens; + + public: + /** Adds a space-delimited list of tokens to the token list. + * @param tokenlist The list of space-delimited tokens to add. + */ + void AddList(const std::string& tokenlist); + + /** Adds a single token to the token list. + * @param token The token to add. + */ + void Add(const std::string& token); + + /** Removes all tokens from the token list. */ + void Clear(); + + /** Determines whether the specified token exists in the token list. + * @param token The token to search for. + */ + bool Contains(const std::string& token) const; + + /** Removes the specified token from the token list. + * @param token The token to remove. + */ + void Remove(const std::string& token); + + /** Retrieves a string which represents the contents of this token list. */ + std::string ToString() const; + + /** Determines whether the specified token list contains the same tokens as this instance. + * @param other The tokenlist to compare against. + * @return True if the token lists are equal; otherwise, false. + */ + bool operator==(const TokenList& other) const; +}; diff --git a/include/typedefs.h b/include/typedefs.h index 03593d40f..20fc596be 100644 --- a/include/typedefs.h +++ b/include/typedefs.h @@ -19,127 +19,97 @@ */ -#ifndef TYPEDEFS_H -#define TYPEDEFS_H +#pragma once class BanCacheManager; -class BanItem; class BufferedSocket; class Channel; class Command; -class ConfigReader; +class ConfigStatus; class ConfigTag; -class DNSHeader; -class DNSRequest; class Extensible; class FakeUser; class InspIRCd; class Invitation; -class InviteBase; +class IOHookProvider; class LocalUser; class Membership; class Module; class OperInfo; +class ProtocolServer; class RemoteUser; +class Server; class ServerConfig; class ServerLimits; class Thread; class User; -class UserResolver; class XLine; class XLineManager; class XLineFactory; struct ConnectClass; struct ModResult; -struct ResourceRecord; + +namespace ClientProtocol +{ + class Event; + class EventProvider; + class Message; + class MessageTagProvider; + class Serializer; + + typedef std::vector<Message*> MessageList; + typedef std::vector<std::string> ParamList; + typedef std::string SerializedMessage; + + struct MessageTagData + { + MessageTagProvider* tagprov; + std::string value; + void* provdata; + + MessageTagData(MessageTagProvider* prov, const std::string& val, void* data = NULL); + }; + + /** Map of message tag values and providers keyed by their name. + * Sorted in descending order to ensure tag names beginning with symbols (such as '+') come later when iterating + * the container than tags with a normal name. + */ + typedef insp::flat_map<std::string, MessageTagData, std::greater<std::string> > TagMap; +} #include "hashcomp.h" #include "base.h" -#ifdef HASHMAP_DEPRECATED - typedef nspace::hash_map<std::string, User*, nspace::insensitive, irc::StrHashComp> user_hash; - typedef nspace::hash_map<std::string, Channel*, nspace::insensitive, irc::StrHashComp> chan_hash; -#else - typedef nspace::hash_map<std::string, User*, nspace::hash<std::string>, irc::StrHashComp> user_hash; - typedef nspace::hash_map<std::string, Channel*, nspace::hash<std::string>, irc::StrHashComp> chan_hash; -#endif - -/** A list holding local users, this is the type of UserManager::local_users - */ -typedef std::list<LocalUser*> LocalUserList; - -/** A list of failed port bindings, used for informational purposes on startup */ -typedef std::vector<std::pair<std::string, std::string> > FailedPortList; - -/** Holds a complete list of all channels to which a user has been invited and has not yet joined, and the time at which they'll expire. - */ -typedef std::vector<Invitation*> InviteList; +typedef TR1NS::unordered_map<std::string, User*, irc::insensitive, irc::StrHashComp> user_hash; +typedef TR1NS::unordered_map<std::string, Channel*, irc::insensitive, irc::StrHashComp> chan_hash; -/** Holds a complete list of all allow and deny tags from the configuration file (connection classes) +/** List of channels to consider when building the neighbor list of a user */ -typedef std::vector<reference<ConnectClass> > ClassVector; - -/** Typedef for the list of user-channel records for a user - */ -typedef std::set<Channel*> UserChanList; - -/** Shorthand for an iterator into a UserChanList - */ -typedef UserChanList::iterator UCListIter; - -/** Holds a complete ban list - */ -typedef std::vector<BanItem> BanList; - -/** A list of custom modes parameters on a channel - */ -typedef std::map<char,std::string> CustomModeList; +typedef std::vector<Membership*> IncludeChanList; /** A cached text file stored with its contents as lines */ typedef std::vector<std::string> file_cache; -/** A configuration key and value pair +/** A mapping of configuration keys to their assigned values. */ -typedef std::pair<std::string, std::string> KeyVal; +typedef insp::flat_map<std::string, std::string, irc::insensitive_swo> ConfigItems; /** The entire configuration */ -typedef std::multimap<std::string, reference<ConfigTag> > ConfigDataHash; +typedef std::multimap<std::string, reference<ConfigTag>, irc::insensitive_swo> ConfigDataHash; /** Iterator of ConfigDataHash */ typedef ConfigDataHash::const_iterator ConfigIter; /** Iterator pair, used for tag-name ranges */ typedef std::pair<ConfigIter,ConfigIter> ConfigTagList; -/** Index of valid oper blocks and types */ -typedef std::map<std::string, reference<OperInfo> > OperIndex; - /** Files read by the configuration */ typedef std::map<std::string, file_cache> ConfigFileCache; -/** A hash of commands used by the core - */ -#ifdef HASHMAP_DEPRECATED - typedef nspace::hash_map<std::string, Command*, nspace::insensitive, irc::StrHashComp> Commandtable; -#else - typedef nspace::hash_map<std::string, Command*, nspace::hash<std::string>, irc::StrHashComp> Commandtable; -#endif - -/** Membership list of a channel */ -typedef std::map<User*, Membership*> UserMembList; -/** Iterator of UserMembList */ -typedef UserMembList::iterator UserMembIter; -/** const Iterator of UserMembList */ -typedef UserMembList::const_iterator UserMembCIter; - /** Generic user list, used for exceptions */ typedef std::set<User*> CUList; -/** A set of strings. - */ -typedef std::vector<std::string> string_list; - /** Contains an ident and host split into two strings */ typedef std::pair<std::string, std::string> IdentHostPair; @@ -150,7 +120,7 @@ typedef std::map<std::string, XLineFactory*> XLineFactMap; /** A map of XLines indexed by string */ -typedef std::map<irc::string, XLine *> XLineLookup; +typedef std::map<std::string, XLine*, irc::insensitive_swo> XLineLookup; /** A map of XLineLookup maps indexed by string */ @@ -164,6 +134,7 @@ typedef XLineContainer::iterator ContainerIter; */ typedef XLineLookup::iterator LookupIter; - -#endif - +namespace Stats +{ + class Context; +} diff --git a/include/uid.h b/include/uid.h index 17061bdee..772c8a716 100644 --- a/include/uid.h +++ b/include/uid.h @@ -16,12 +16,44 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#pragma once -/** - * This is the maximum length of a UUID (unique user identifier). - * This length is set in compliance with TS6 protocol, and really should not be changed. Ever. - * It allows for a lot of clients as-is. -- w00t. - */ -#define UUID_LENGTH 10 +class TestSuite; + +class CoreExport UIDGenerator +{ + friend class TestSuite; + + /** Holds the current UID. Used to generate the next one. + */ + std::string current_uid; + + /** Increments the current UID by one. + */ + void IncrementUID(unsigned int pos); + + public: + /** + * This is the maximum length of a UUID (unique user identifier). + * This length is set in compliance with TS6 protocol, and really should not be changed. Ever. + * It allows for a lot of clients as-is. -- w00t. + */ + static const unsigned int UUID_LENGTH = 9; + + /** Initializes this UID generator with the given SID + * @param sid SID that conforms to InspIRCd::IsSID() + */ + void init(const std::string& sid); + /** Returns the next available UID for this server. + */ + std::string GetUID(); + /** Generates a pseudorandom SID based on a servername and a description + * Guaranteed to return the same if invoked with the same parameters + * @param servername The server name to use as seed + * @param serverdesc The server description to use as seed + * @return A valid SID + */ + static std::string GenerateSID(const std::string& servername, const std::string& serverdesc); +}; diff --git a/include/usermanager.h b/include/usermanager.h index 2a9d6b47b..531d50773 100644 --- a/include/usermanager.h +++ b/include/usermanager.h @@ -17,93 +17,112 @@ */ -#ifndef USERMANAGER_H -#define USERMANAGER_H +#pragma once #include <list> -/** A list of ip addresses cross referenced against clone counts */ -typedef std::map<irc::sockets::cidr_mask, unsigned int> clonemap; - -class CoreExport UserManager +class CoreExport UserManager : public fakederef<UserManager> { + public: + struct CloneCounts + { + unsigned int global; + unsigned int local; + CloneCounts() : global(0), local(0) { } + }; + + /** Container that maps IP addresses to clone counts + */ + typedef std::map<irc::sockets::cidr_mask, CloneCounts> CloneMap; + + /** Sequence container in which each element is a User* + */ + typedef std::vector<User*> OperList; + + /** A list holding local users + */ + typedef insp::intrusive_list<LocalUser> LocalList; + private: - /** Map of local ip addresses for clone counting + /** Map of IP addresses for clone counting + */ + CloneMap clonemap; + + /** A CloneCounts that contains zero for both local and global + */ + const CloneCounts zeroclonecounts; + + /** Local client list, a list containing only local clients + */ + LocalList local_users; + + /** Last used already sent id, used when sending messages to neighbors to help determine whether the message has + * been sent to a particular user or not. See User::ForEachNeighbor() for more info. */ - clonemap local_clones; + already_sent_t already_sent_id; + public: + /** Constructor, initializes variables + */ UserManager(); - ~UserManager() - { - for (user_hash::iterator i = clientlist->begin();i != clientlist->end();i++) - { - delete i->second; - } - clientlist->clear(); - delete clientlist; - delete uuidlist; - } - - /** Client list, a hash_map containing all clients, local and remote + /** Destructor, destroys all users in clientlist */ - user_hash* clientlist; + ~UserManager(); - /** Client list stored by UUID. Contains all clients, and is updated - * automatically by the constructor and destructor of User. + /** Nickname string -> User* map. Contains all users, including unregistered ones. */ - user_hash* uuidlist; + user_hash clientlist; - /** Local client list, a list containing only local clients + /** UUID -> User* map. Contains all users, including unregistered ones. */ - LocalUserList local_users; + user_hash uuidlist; /** Oper list, a vector containing all local and remote opered users */ - std::list<User*> all_opers; + OperList all_opers; /** Number of unregistered users online right now. * (Unregistered means before USER/NICK/dns) */ unsigned int unregistered_count; - /** Number of elements in local_users + /** Perform background user events for all local users such as PING checks, registration timeouts, + * penalty management and recvq processing for users who have data in their recvq due to throttling. */ - unsigned int local_count; + void DoBackgroundUserStuff(); - /** Map of global ip addresses for clone counting - * XXX - this should be private, but m_clones depends on it currently. + /** Returns true when all modules have done pre-registration checks on a user + * @param user The user to verify + * @return True if all modules have finished checking this user */ - clonemap global_clones; + bool AllModulesReportReady(LocalUser* user); - /** Add a client to the system. - * This will create a new User, insert it into the user_hash, - * initialize it as not yet registered, and add it to the socket engine. - * @param socket The socket id (file descriptor) this user is on - * @param via The socket that this user connected using + /** Handle a client connection. + * Creates a new LocalUser object, inserts it into the appropriate containers, + * initializes it as not yet registered, and adds it to the socket engine. + * + * The new user may immediately be quit after being created, for example if the user limit + * is reached or if the user is banned. + * @param socket File descriptor of the connection + * @param via Listener socket that this user connected to * @param client The IP address and client port of the user * @param server The server IP address and port used by the user - * @return This function has no return value, but a call to AddClient may remove the user. */ void AddUser(int socket, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server); - /** Disconnect a user gracefully + /** Disconnect a user gracefully. + * When this method returns the user provided will be quit, but the User object will continue to be valid and will be deleted at the end of the current main loop iteration. * @param user The user to remove * @param quitreason The quit reason to show to normal users - * @param operreason The quit reason to show to opers - * @return Although this function has no return type, on exit the user provided will no longer exist. + * @param operreason The quit reason to show to opers, can be NULL if same as quitreason */ - void QuitUser(User *user, const std::string &quitreason, const char* operreason = ""); + void QuitUser(User* user, const std::string& quitreason, const std::string* operreason = NULL); - /** Add a user to the local clone map + /** Add a user to the clone map * @param user The user to add */ - void AddLocalClone(User *user); - - /** Add a user to the global clone map - * @param user The user to add - */ - void AddGlobalClone(User *user); + void AddClone(User* user); /** Remove all clone counts from the user, you should * use this if you change the user's IP address @@ -112,53 +131,57 @@ class CoreExport UserManager */ void RemoveCloneCounts(User *user); - /** Rebuild clone counts + /** Rebuild clone counts. Required when \<cidr> settings change. */ void RehashCloneCounts(); - /** Return the number of global clones of this user - * @param user The user to get a count for - * @return The global clone count of this user + /** Return the number of local and global clones of this user + * @param user The user to get the clone counts for + * @return The clone counts of this user. The returned reference is volatile - you + * must assume that it becomes invalid as soon as you call any function other than + * your own. */ - unsigned long GlobalCloneCount(User *user); + const CloneCounts& GetCloneCounts(User* user) const; - /** Return the number of local clones of this user - * @param user The user to get a count for - * @return The local clone count of this user + /** Return a map containg IP addresses and their clone counts + * @return The clone count map */ - unsigned long LocalCloneCount(User *user); + const CloneMap& GetCloneMap() const { return clonemap; } - /** Return a count of users, unknown and known connections - * @return The number of users + /** Return a count of all global users, unknown and known connections + * @return The number of users on the network, including local unregistered users */ - unsigned int UserCount(); + unsigned int UserCount() const { return this->clientlist.size(); } - /** Return a count of fully registered connections only - * @return The number of registered users + /** Return a count of fully registered connections on the network + * @return The number of registered users on the network */ - unsigned int RegisteredUserCount(); + unsigned int RegisteredUserCount() { return this->clientlist.size() - this->UnregisteredUserCount(); } - /** Return a count of opered (umode +o) users only - * @return The number of opers + /** Return a count of opered (umode +o) users on the network + * @return The number of opers on the network */ - unsigned int OperCount(); + unsigned int OperCount() const { return this->all_opers.size(); } - /** Return a count of unregistered (before NICK/USER) users only - * @return The number of unregistered (unknown) connections + /** Return a count of local unregistered (before NICK/USER) users + * @return The number of local unregistered (unknown) connections */ - unsigned int UnregisteredUserCount(); + unsigned int UnregisteredUserCount() const { return this->unregistered_count; } - /** Return a count of local users on this server only - * @return The number of local users + /** Return a count of local registered users + * @return The number of registered local users */ - unsigned int LocalUserCount(); - - + unsigned int LocalUserCount() const { return (this->local_users.size() - this->UnregisteredUserCount()); } + /** Get a hash map containing all users, keyed by their nickname + * @return A hash map mapping nicknames to User pointers + */ + user_hash& GetUsers() { return clientlist; } - /** Number of users with a certain mode set on them + /** Get a list containing all local users + * @return A const list of local users */ - int ModeCount(const char mode); + const LocalList& GetLocalUsers() const { return local_users; } /** Send a server notice to all local users * @param text The text format string to send @@ -166,11 +189,8 @@ class CoreExport UserManager */ void ServerNoticeAll(const char* text, ...) CUSTOM_PRINTF(2, 3); - /** Send a server message (PRIVMSG) to all local users - * @param text The text format string to send - * @param ... The format arguments + /** Retrieves the next already sent id, guaranteed to be not equal to any user's already_sent field + * @return Next already_sent id */ - void ServerPrivmsgAll(const char* text, ...) CUSTOM_PRINTF(2, 3); + already_sent_t NextAlreadySentId(); }; - -#endif diff --git a/include/users.h b/include/users.h index 88abfbcd1..e8f5399e8 100644 --- a/include/users.h +++ b/include/users.h @@ -22,12 +22,10 @@ */ -#ifndef USERS_H -#define USERS_H +#pragma once #include "socket.h" #include "inspsocket.h" -#include "dns.h" #include "mode.h" #include "membership.h" @@ -42,19 +40,6 @@ enum ClassTypes { CC_NAMED = 2 }; -/** RFC1459 channel modes - */ -enum UserModes { - /** +s: Server notice mask */ - UM_SNOMASK = 's' - 65, - /** +w: WALLOPS */ - UM_WALLOPS = 'w' - 65, - /** +i: Invisible */ - UM_INVISIBLE = 'i' - 65, - /** +o: Operator */ - UM_OPERATOR = 'o' - 65 -}; - /** Registration state of a user, e.g. * have they sent USER, NICK, PASS yet? */ @@ -146,6 +131,15 @@ struct CoreExport ConnectClass : public refcountbase */ unsigned long limit; + /** If set to true, no user DNS lookups are to be performed + */ + bool resolvehostnames; + + /** + * If non-empty the server ports which this user has to be using + */ + insp::flat_set<int> ports; + /** Create a new connect class with no settings. */ ConnectClass(ConfigTag* tag, char type, const std::string& mask); @@ -250,12 +244,40 @@ class CoreExport User : public Extensible */ std::string cachedip; + /** If set then the hostname which is displayed to users. */ + std::string displayhost; + + /** The real hostname of this user. */ + std::string realhost; + + /** The real name of this user. */ + std::string realname; + + /** The user's mode list. + * Much love to the STL for giving us an easy to use bitset, saving us RAM. + * if (modes[modeid]) is set, then the mode is set. + * For example, to work out if mode +i is set, we check the field + * User::modes[invisiblemode->modeid] == true. + */ + std::bitset<ModeParser::MODEID_MAX> modes; + public: + /** To execute a function for each local neighbor of a user, inherit from this class and + * pass an instance of it to User::ForEachNeighbor(). + */ + class ForEachNeighborHandler + { + public: + /** Method to execute for each local neighbor of a user. + * Derived classes must implement this. + * @param user Current neighbor + */ + virtual void Execute(LocalUser* user) = 0; + }; - /** Hostname of connection. - * This should be valid as per RFC1035. + /** List of Memberships for this user */ - std::string host; + typedef insp::intrusive_list<Membership> ChanList; /** Time that the object was instantiated (used for TS calculation etc) */ @@ -267,10 +289,6 @@ class CoreExport User : public Extensible */ time_t signon; - /** Time that the connection last sent a message, used to calculate idle time - */ - time_t idle_lastmsg; - /** Client address that the user is connected from. * Do not modify this value directly, use SetClientIP() to change it. * Port is not valid for remote users. @@ -293,27 +311,6 @@ class CoreExport User : public Extensible */ std::string ident; - /** The host displayed to non-opers (used for cloaking etc). - * This usually matches the value of User::host. - */ - std::string dhost; - - /** The users full name (GECOS). - */ - std::string fullname; - - /** The user's mode list. - * NOT a null terminated string. - * Also NOT an array. - * Much love to the STL for giving us an easy to use bitset, saving us RAM. - * if (modes[modeletter-65]) is set, then the mode is - * set, for example, to work out if mode +s is set, we check the field - * User::modes['s'-65] != 0. - * The following RFC characters o, w, s, i have constants defined via an - * enum, such as UM_SERVERNOTICE and UM_OPETATOR. - */ - std::bitset<64> modes; - /** What snomasks are set on this user. * This functions the same as the above modes. */ @@ -321,11 +318,11 @@ class CoreExport User : public Extensible /** Channels this user is on */ - UserChanList chans; + ChanList chans; /** The server the user is connected to. */ - const std::string server; + Server* server; /** The user's away message. * If this string is empty, the user is not marked as away. @@ -333,7 +330,7 @@ class CoreExport User : public Extensible std::string awaymsg; /** Time the user last went away. - * This is ONLY RELIABLE if user IS_AWAY()! + * This is ONLY RELIABLE if user IsAway()! */ time_t awaytime; @@ -347,16 +344,6 @@ class CoreExport User : public Extensible */ unsigned int registered:3; - /** True when DNS lookups are completed. - * The UserResolver classes res_forward and res_reverse will - * set this value once they complete. - */ - unsigned int dns_done:1; - - /** Whether or not to send an snotice about this user's quitting - */ - unsigned int quietquit:1; - /** If this is set to true, then all socket operations for the user * are dropped into the bit-bucket. * This value is set by QuitUser, and is not needed seperately from that call. @@ -364,27 +351,27 @@ class CoreExport User : public Extensible */ unsigned int quitting:1; - /** Recursion fix: user is out of SendQ and will be quit as soon as possible. - * This can't be handled normally because QuitUser itself calls Write on other - * users, which could trigger their SendQ to overrun. - */ - unsigned int quitting_sendq:1; + /** What type of user is this? */ + const UserType usertype:2; - /** This is true if the user matched an exception (E:Line). It is used to save time on ban checks. + /** Get client IP string from sockaddr, using static internal buffer + * @return The IP string */ - unsigned int exempt:1; + const std::string& GetIPString(); - /** has the user responded to their previous ping? + /** Retrieves this user's hostname. + * @param uncloak If true then return the real host; otherwise, the display host. */ - unsigned int lastping:1; + const std::string& GetHost(bool uncloak) const; - /** What type of user is this? */ - const unsigned int usertype:2; + /** Retrieves this user's displayed hostname. */ + const std::string& GetDisplayedHost() const; - /** Get client IP string from sockaddr, using static internal buffer - * @return The IP string - */ - const char* GetIPString(); + /** Retrieves this user's real hostname. */ + const std::string& GetRealHost() const; + + /** Retrieves this user's real name. */ + const std::string& GetRealName() const; /** Get CIDR mask, using default range, for this user */ @@ -393,20 +380,14 @@ class CoreExport User : public Extensible /** Sets the client IP for this user * @return true if the conversion was successful */ - virtual bool SetClientIP(const char* sip, bool recheck_eline = true); + virtual bool SetClientIP(const std::string& address, bool recheck_eline = true); virtual void SetClientIP(const irc::sockets::sockaddrs& sa, bool recheck_eline = true); /** Constructor * @throw CoreException if the UID allocated to the user already exists */ - User(const std::string &uid, const std::string& srv, int objtype); - - /** Check if the user matches a G or K line, and disconnect them if they do. - * @param doZline True if ZLines should be checked (if IP has changed since initial connect) - * Returns true if the user matched a ban, false else. - */ - bool CheckLines(bool doZline = false); + User(const std::string& uid, Server* srv, UserType objtype); /** Returns the full displayed host of the user * This member function returns the hostname of the user as seen by other users @@ -428,18 +409,17 @@ class CoreExport User : public Extensible */ void InvalidateCache(); - /** Create a displayable mode string for this users snomasks - * @return The notice mask character sequence + /** Returns whether this user is currently away or not. If true, + * further information can be found in User::awaymsg and User::awaytime + * @return True if the user is away, false otherwise */ - const char* FormatNoticeMasks(); + bool IsAway() const { return (!awaymsg.empty()); } - /** Process a snomask modifier string, e.g. +abc-de - * @param sm A sequence of notice mask characters - * @return The cleaned mode sequence which can be output, - * e.g. in the above example if masks c and e are not - * valid, this function will return +ab-d + /** Returns whether this user is an oper or not. If true, + * oper information can be obtained from User::oper + * @return True if the user is an oper, false otherwise */ - std::string ProcessNoticeMasks(const char *sm); + bool IsOper() const { return oper; } /** Returns true if a notice mask is set * @param sm A notice mask character to check @@ -447,28 +427,28 @@ class CoreExport User : public Extensible */ bool IsNoticeMaskSet(unsigned char sm); - /** Changed a specific notice mask value - * @param sm The server notice mask to change - * @param value An on/off value for this mask + /** Get the mode letters of modes set on the user as a string. + * @param includeparams True to get the parameters of the modes as well. Defaults to false. + * @return Mode letters of modes set on the user and optionally the parameters of those modes, if any. + * The returned string always begins with a '+' character. If the user has no modes set, "+" is returned. */ - void SetNoticeMask(unsigned char sm, bool value); - - /** Create a displayable mode string for this users umodes - * @param showparameters The mode string - */ - const char* FormatModes(bool showparameters = false); + std::string GetModeLetters(bool includeparams = false) const; /** Returns true if a specific mode is set * @param m The user mode * @return True if the mode is set */ - bool IsModeSet(unsigned char m); + bool IsModeSet(unsigned char m) const; + bool IsModeSet(const ModeHandler* mh) const; + bool IsModeSet(const ModeHandler& mh) const { return IsModeSet(&mh); } + bool IsModeSet(UserModeReference& moderef) const; /** Set a specific usermode to on or off - * @param m The user mode + * @param mh The user mode * @param value On or off setting of the mode */ - void SetMode(unsigned char m, bool value); + void SetMode(ModeHandler* mh, bool value); + void SetMode(ModeHandler& mh, bool value) { SetMode(&mh, value); } /** Returns true or false for if a user can execute a privilaged oper command. * This is done by looking up their oper type from User::oper, then referencing @@ -491,17 +471,10 @@ class CoreExport User : public Extensible /** Returns true or false if a user can set a privileged user or channel mode. * This is done by looking up their oper type from User::oper, then referencing * this to their oper classes, and checking the modes they can set. - * @param mode The mode the check - * @param type ModeType (MODETYPE_CHANNEL or MODETYPE_USER). + * @param mh Mode to check * @return True if the user can set or unset this mode. */ - virtual bool HasModePermission(unsigned char mode, ModeType type); - - /** Creates a wildcard host. - * Takes a buffer to use and fills the given buffer with the host in the format *!*\@hostname - * @return The wildcarded hostname in *!*\@host form - */ - char* MakeWildHost(); + virtual bool HasModePermission(const ModeHandler* mh) const; /** Creates a usermask with real host. * Takes a buffer to use and fills the given buffer with the hostmask in the format user\@host @@ -515,122 +488,145 @@ class CoreExport User : public Extensible */ const std::string& MakeHostIP(); - /** Add the user to WHOWAS system - */ - void AddToWhoWas(); - /** Oper up the user using the given opertype. * This will also give the +o usermode. */ void Oper(OperInfo* info); - /** Force a nickname change. - * If the nickname change fails (for example, because the nick in question - * already exists) this function will return false, and you must then either - * output an error message, or quit the user for nickname collision. - * @param newnick The nickname to change to - * @return True if the nickchange was successful. - */ - inline bool ForceNickChange(const char* newnick) { return ChangeNick(newnick, true); } - /** Oper down. * This will clear the +o usermode and unset the user's oper type */ void UnOper(); - /** Write text to this user, appending CR/LF. Works on local users only. - * @param text A std::string to send to the user + /** Sends a server notice to this user. + * @param text The contents of the message to send. */ - virtual void Write(const std::string &text); + void WriteNotice(const std::string& text); - /** Write text to this user, appending CR/LF. - * Works on local users only. - * @param text The format string for text to send to the user - * @param ... POD-type format arguments + /** Send a NOTICE message from the local server to the user. + * @param text Text to send */ - virtual void Write(const char *text, ...) CUSTOM_PRINTF(2, 3); + virtual void WriteRemoteNotice(const std::string& text); - /** Write text to this user, appending CR/LF and prepending :server.name - * Works on local users only. - * @param text A std::string to send to the user - */ - void WriteServ(const std::string& text); - - /** Write text to this user, appending CR/LF and prepending :server.name - * Works on local users only. - * @param text The format string for text to send to the user - * @param ... POD-type format arguments - */ - void WriteServ(const char* text, ...) CUSTOM_PRINTF(2, 3); + virtual void WriteRemoteNumeric(const Numeric::Numeric& numeric); - void WriteNumeric(unsigned int numeric, const char* text, ...) CUSTOM_PRINTF(3, 4); + template <typename T1> + void WriteRemoteNumeric(unsigned int numeric, T1 p1) + { + Numeric::Numeric n(numeric); + n.push(p1); + WriteRemoteNumeric(n); + } - void WriteNumeric(unsigned int numeric, const std::string &text); + template <typename T1, typename T2> + void WriteRemoteNumeric(unsigned int numeric, T1 p1, T2 p2) + { + Numeric::Numeric n(numeric); + n.push(p1); + n.push(p2); + WriteRemoteNumeric(n); + } - /** Write text to this user, appending CR/LF and prepending :nick!user\@host of the user provided in the first parameter. - * @param user The user to prepend the :nick!user\@host of - * @param text A std::string to send to the user - */ - void WriteFrom(User *user, const std::string &text); + template <typename T1, typename T2, typename T3> + void WriteRemoteNumeric(unsigned int numeric, T1 p1, T2 p2, T3 p3) + { + Numeric::Numeric n(numeric); + n.push(p1); + n.push(p2); + n.push(p3); + WriteRemoteNumeric(n); + } - /** Write text to this user, appending CR/LF and prepending :nick!user\@host of the user provided in the first parameter. - * @param user The user to prepend the :nick!user\@host of - * @param text The format string for text to send to the user - * @param ... POD-type format arguments - */ - void WriteFrom(User *user, const char* text, ...) CUSTOM_PRINTF(3, 4); + template <typename T1, typename T2, typename T3, typename T4> + void WriteRemoteNumeric(unsigned int numeric, T1 p1, T2 p2, T3 p3, T4 p4) + { + Numeric::Numeric n(numeric); + n.push(p1); + n.push(p2); + n.push(p3); + n.push(p4); + WriteRemoteNumeric(n); + } - /** Write text to the user provided in the first parameter, appending CR/LF, and prepending THIS user's :nick!user\@host. - * @param dest The user to route the message to - * @param data A std::string to send to the user - */ - void WriteTo(User *dest, const std::string &data); + template <typename T1, typename T2, typename T3, typename T4, typename T5> + void WriteRemoteNumeric(unsigned int numeric, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) + { + Numeric::Numeric n(numeric); + n.push(p1); + n.push(p2); + n.push(p3); + n.push(p4); + n.push(p5); + WriteRemoteNumeric(n); + } - /** Write text to the user provided in the first parameter, appending CR/LF, and prepending THIS user's :nick!user\@host. - * @param dest The user to route the message to - * @param data The format string for text to send to the user - * @param ... POD-type format arguments - */ - void WriteTo(User *dest, const char *data, ...) CUSTOM_PRINTF(3, 4); + void WriteNumeric(const Numeric::Numeric& numeric); - /** Write to all users that can see this user (including this user in the list if include_self is true), appending CR/LF - * @param line A std::string to send to the users - * @param include_self Should the message be sent back to the author? - */ - void WriteCommonRaw(const std::string &line, bool include_self = true); + template <typename T1> + void WriteNumeric(unsigned int numeric, T1 p1) + { + Numeric::Numeric n(numeric); + n.push(p1); + WriteNumeric(n); + } - /** Write to all users that can see this user (including this user in the list), appending CR/LF - * @param text The format string for text to send to the users - * @param ... POD-type format arguments - */ - void WriteCommon(const char* text, ...) CUSTOM_PRINTF(2, 3); + template <typename T1, typename T2> + void WriteNumeric(unsigned int numeric, T1 p1, T2 p2) + { + Numeric::Numeric n(numeric); + n.push(p1); + n.push(p2); + WriteNumeric(n); + } - /** Write to all users that can see this user (not including this user in the list), appending CR/LF - * @param text The format string for text to send to the users - * @param ... POD-type format arguments - */ - void WriteCommonExcept(const char* text, ...) CUSTOM_PRINTF(2, 3); + template <typename T1, typename T2, typename T3> + void WriteNumeric(unsigned int numeric, T1 p1, T2 p2, T3 p3) + { + Numeric::Numeric n(numeric); + n.push(p1); + n.push(p2); + n.push(p3); + WriteNumeric(n); + } - /** Write a quit message to all common users, as in User::WriteCommonExcept but with a specific - * quit message for opers only. - * @param normal_text Normal user quit message - * @param oper_text Oper only quit message - */ - void WriteCommonQuit(const std::string &normal_text, const std::string &oper_text); + template <typename T1, typename T2, typename T3, typename T4> + void WriteNumeric(unsigned int numeric, T1 p1, T2 p2, T3 p3, T4 p4) + { + Numeric::Numeric n(numeric); + n.push(p1); + n.push(p2); + n.push(p3); + n.push(p4); + WriteNumeric(n); + } - /** Dump text to a user target, splitting it appropriately to fit - * @param LinePrefix text to prefix each complete line with - * @param TextStream the text to send to the user - */ - void SendText(const std::string &LinePrefix, std::stringstream &TextStream); + template <typename T1, typename T2, typename T3, typename T4, typename T5> + void WriteNumeric(unsigned int numeric, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) + { + Numeric::Numeric n(numeric); + n.push(p1); + n.push(p2); + n.push(p3); + n.push(p4); + n.push(p5); + WriteNumeric(n); + } - /** Write to the user, routing the line if the user is remote. + /** Write to all users that can see this user (including this user in the list if include_self is true), appending CR/LF + * @param protoev Protocol event to send, may contain any number of messages. + * @param include_self Should the message be sent back to the author? */ - virtual void SendText(const std::string& line) = 0; + void WriteCommonRaw(ClientProtocol::Event& protoev, bool include_self = true); - /** Write to the user, routing the line if the user is remote. + /** Execute a function once for each local neighbor of this user. By default, the neighbors of a user are the users + * who have at least one common channel with the user. Modules are allowed to alter the set of neighbors freely. + * This function is used for example to send something conditionally to neighbors, or to send different messages + * to different users depending on their oper status. + * @param handler Function object to call, inherited from ForEachNeighborHandler. + * @param include_self True to include this user in the set of neighbors, false otherwise. + * Modules may override this. Has no effect if this user is not local. */ - void SendText(const char* text, ...) CUSTOM_PRINTF(2, 3); + void ForEachNeighbor(ForEachNeighborHandler& handler, bool include_self = true); /** Return true if the user shares at least one channel with another user * @param other The other user to compare the channel list against @@ -638,87 +634,49 @@ class CoreExport User : public Extensible */ bool SharesChannelWith(User *other); - /** Send fake quit/join messages for host or ident cycle. - * Run this after the item in question has changed. - * You should not need to use this function, call ChangeDisplayedHost instead - * - * @param quitline The entire QUIT line, including the source using the old value + /** Change the displayed hostname of this user. + * @param host The new displayed hostname of this user. + * @return True if the hostname was changed successfully; otherwise, false. */ - void DoHostCycle(const std::string &quitline); + bool ChangeDisplayedHost(const std::string& host); - /** Change the displayed host of a user. - * ALWAYS use this function, rather than writing User::dhost directly, - * as this triggers module events allowing the change to be syncronized to - * remote servers. This will also emulate a QUIT and rejoin (where configured) - * before setting their host field. - * @param host The new hostname to set - * @return True if the change succeeded, false if it didn't + /** Change the real hostname of this user. + * @param host The new real hostname of this user. + * @param resetdisplay Whether to reset the display host to this value. */ - bool ChangeDisplayedHost(const char* host); + void ChangeRealHost(const std::string& host, bool resetdisplay); /** Change the ident (username) of a user. * ALWAYS use this function, rather than writing User::ident directly, - * as this correctly causes the user to seem to quit (where configured) - * before setting their ident field. + * as this triggers module events allowing the change to be syncronized to + * remote servers. * @param newident The new ident to set * @return True if the change succeeded, false if it didn't */ - bool ChangeIdent(const char* newident); + bool ChangeIdent(const std::string& newident); /** Change a users realname field. - * ALWAYS use this function, rather than writing User::fullname directly, - * as this triggers module events allowing the change to be syncronized to - * remote servers. - * @param gecos The user's new realname + * @param real The user's new real name * @return True if the change succeeded, false if otherwise */ - bool ChangeName(const char* gecos); + bool ChangeRealName(const std::string& real); /** Change a user's nick - * @param newnick The new nick - * @param force True if the change is being forced (should not be blocked by modes like +N) + * @param newnick The new nick. If equal to the users uuid, the nick change always succeeds. + * @param newts The time at which this nick change happened. * @return True if the change succeeded */ - bool ChangeNick(const std::string& newnick, bool force = false); - - /** Send a command to all local users from this user - * The command given must be able to send text with the - * first parameter as a servermask (e.g. $*), so basically - * you should use PRIVMSG or NOTICE. - * @param command the command to send - * @param text The text format string to send - * @param ... Format arguments - */ - void SendAll(const char* command, const char* text, ...) CUSTOM_PRINTF(3, 4); - - /** Compile a channel list for this user. Used internally by WHOIS - * @param source The user to prepare the channel list for - * @param spy Whether to return the spy channel list rather than the normal one - * @return This user's channel list - */ - std::string ChannelList(User* source, bool spy); - - /** Split the channel list in cl which came from dest, and spool it to this user - * Used internally by WHOIS - * @param dest The user the original channel list came from - * @param cl The channel list as a string obtained from User::ChannelList() - */ - void SplitChanList(User* dest, const std::string &cl); + bool ChangeNick(const std::string& newnick, time_t newts = 0); /** Remove this user from all channels they are on, and delete any that are now empty. * This is used by QUIT, and will not send part messages! */ void PurgeEmptyChannels(); - /** Get the connect class which this user belongs to. NULL for remote users. - * @return A pointer to this user's connect class. - */ - virtual ConnectClass* GetClass(); - /** Default destructor */ virtual ~User(); - virtual CullResult cull(); + CullResult cull() CXX11_OVERRIDE; }; class CoreExport UserIOHandler : public StreamSocket @@ -726,8 +684,9 @@ class CoreExport UserIOHandler : public StreamSocket public: LocalUser* const user; UserIOHandler(LocalUser* me) : user(me) {} - void OnDataReady(); - void OnError(BufferedSocketError error); + void OnDataReady() CXX11_OVERRIDE; + void OnSetEndPoint(const irc::sockets::sockaddrs& local, const irc::sockets::sockaddrs& remote) CXX11_OVERRIDE; + void OnError(BufferedSocketError error) CXX11_OVERRIDE; /** Adds to the user's write buffer. * You may add any amount of text up to this users sendq value, if you exceed the @@ -739,17 +698,33 @@ class CoreExport UserIOHandler : public StreamSocket typedef unsigned int already_sent_t; -class CoreExport LocalUser : public User, public InviteBase +class CoreExport LocalUser : public User, public insp::intrusive_list_node<LocalUser> { + /** Add a serialized message to the send queue of the user. + * @param serialized Bytes to add. + */ + void Write(const ClientProtocol::SerializedMessage& serialized); + + /** Send a protocol event to the user, consisting of one or more messages. + * @param protoev Event to send, may contain any number of messages. + * @param msglist Message list used temporarily internally to pass to hooks and store messages + * before Write(). + */ + void Send(ClientProtocol::Event& protoev, ClientProtocol::MessageList& msglist); + + /** Message list, can be passed to the two parameter Send(). + */ + static ClientProtocol::MessageList sendmsglist; + public: LocalUser(int fd, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server); - CullResult cull(); + CullResult cull() CXX11_OVERRIDE; UserIOHandler eh; - /** Position in UserManager::local_users + /** Serializer to use when communicating with the user */ - LocalUserList::iterator localuseriter; + ClientProtocol::Serializer* serializer; /** Stats counter for bytes inbound */ @@ -777,11 +752,14 @@ class CoreExport LocalUser : public User, public InviteBase */ reference<ConnectClass> MyClass; - ConnectClass* GetClass(); + /** Get the connect class which this user belongs to. + * @return A pointer to this user's connect class. + */ + ConnectClass* GetClass() const { return MyClass; } /** Call this method to find the matching \<connect> for a user, and to check them against it. */ - void CheckClass(); + void CheckClass(bool clone_count = true); /** Server address and port that this user is connected to. */ @@ -792,27 +770,40 @@ class CoreExport LocalUser : public User, public InviteBase */ int GetServerPort(); + /** Recursion fix: user is out of SendQ and will be quit as soon as possible. + * This can't be handled normally because QuitUser itself calls Write on other + * users, which could trigger their SendQ to overrun. + */ + unsigned int quitting_sendq:1; + + /** has the user responded to their previous ping? + */ + unsigned int lastping:1; + + /** This is true if the user matched an exception (E:Line). It is used to save time on ban checks. + */ + unsigned int exempt:1; + /** Used by PING checking code */ time_t nping; + /** Time that the connection last sent a message, used to calculate idle time + */ + time_t idle_lastmsg; + /** This value contains how far into the penalty threshold the user is. * This is used either to enable fake lag or for excess flood quits */ unsigned int CommandFloodPenalty; - static already_sent_t already_sent_id; already_sent_t already_sent; - /** Stored reverse lookup from res_forward. Should not be used after resolution. - */ - std::string stored_host; - - /** Starts a DNS lookup of the user's IP. - * This will cause two UserResolver classes to be instantiated. - * When complete, these objects set User::dns_done to true. + /** Check if the user matches a G or K line, and disconnect them if they do. + * @param doZline True if ZLines should be checked (if IP has changed since initial connect) + * Returns true if the user matched a ban, false else. */ - void StartDNSLookup(); + bool CheckLines(bool doZline = false); /** Use this method to fully connect a user. * This will send the message of the day, check G/K/E lines, etc. @@ -825,39 +816,15 @@ class CoreExport LocalUser : public User, public InviteBase */ void SetClass(const std::string &explicit_name = ""); - bool SetClientIP(const char* sip, bool recheck_eline = true); + bool SetClientIP(const std::string& address, bool recheck_eline = true) CXX11_OVERRIDE; - void SetClientIP(const irc::sockets::sockaddrs& sa, bool recheck_eline = true); + void SetClientIP(const irc::sockets::sockaddrs& sa, bool recheck_eline = true) CXX11_OVERRIDE; - void SendText(const std::string& line); - void Write(const std::string& text); - void Write(const char*, ...) CUSTOM_PRINTF(2, 3); - - /** Returns the list of channels this user has been invited to but has not yet joined. - * @return A list of channels the user is invited to - */ - InviteList& GetInviteList(); - - /** Returns true if a user is invited to a channel. - * @param channel A channel name to look up - * @return True if the user is invited to the given channel + /** Send a NOTICE message from the local server to the user. + * The message will be sent even if the user is connected to a remote server. + * @param text Text to send */ - bool IsInvited(const irc::string &channel); - - /** Adds a channel to a users invite list (invites them to a channel) - * @param channel A channel name to add - * @param timeout When the invite should expire (0 == never) - */ - void InviteTo(const irc::string &channel, time_t timeout); - - /** Removes a channel from a users invite list. - * This member function is called on successfully joining an invite only channel - * to which the user has previously been invited, to clear the invitation. - * @param channel The channel to remove the invite to - */ - void RemoveInvite(const irc::string &channel); - - void RemoveExpiredInvites(); + void WriteRemoteNotice(const std::string& text) CXX11_OVERRIDE; /** Returns true or false for if a user can execute a privilaged oper command. * This is done by looking up their oper type from User::oper, then referencing @@ -865,7 +832,7 @@ class CoreExport LocalUser : public User, public InviteBase * @param command A command (should be all CAPS) * @return True if this user can execute the command */ - bool HasPermission(const std::string &command); + bool HasPermission(const std::string &command) CXX11_OVERRIDE; /** Returns true if a user has a given permission. * This is used to check whether or not users may perform certain actions which admins may not wish to give to @@ -875,39 +842,59 @@ class CoreExport LocalUser : public User, public InviteBase * @param noisy If set to true, the user is notified that they do not have the specified permission where applicable. If false, no notification is sent. * @return True if this user has the permission in question. */ - bool HasPrivPermission(const std::string &privstr, bool noisy = false); + bool HasPrivPermission(const std::string &privstr, bool noisy = false) CXX11_OVERRIDE; /** Returns true or false if a user can set a privileged user or channel mode. * This is done by looking up their oper type from User::oper, then referencing * this to their oper classes, and checking the modes they can set. - * @param mode The mode the check - * @param type ModeType (MODETYPE_CHANNEL or MODETYPE_USER). + * @param mh Mode to check * @return True if the user can set or unset this mode. */ - bool HasModePermission(unsigned char mode, ModeType type); + bool HasModePermission(const ModeHandler* mh) const CXX11_OVERRIDE; + + /** Change nick to uuid, unset REG_NICK and send a nickname overruled numeric. + * This is called when another user (either local or remote) needs the nick of this user and this user + * isn't registered. + */ + void OverruleNick(); + + /** Send a protocol event to the user, consisting of one or more messages. + * @param protoev Event to send, may contain any number of messages. + */ + void Send(ClientProtocol::Event& protoev); + + /** Send a single message to the user. + * @param protoevprov Protocol event provider. + * @param msg Message to send. + */ + void Send(ClientProtocol::EventProvider& protoevprov, ClientProtocol::Message& msg); }; -class CoreExport RemoteUser : public User +class RemoteUser : public User { public: - RemoteUser(const std::string& uid, const std::string& srv) : User(uid, srv, USERTYPE_REMOTE) + RemoteUser(const std::string& uid, Server* srv) : User(uid, srv, USERTYPE_REMOTE) { } - virtual void SendText(const std::string& line); }; class CoreExport FakeUser : public User { public: - FakeUser(const std::string &uid, const std::string& srv) : User(uid, srv, USERTYPE_SERVER) + FakeUser(const std::string& uid, Server* srv) : User(uid, srv, USERTYPE_SERVER) { - nick = srv; + nick = srv->GetName(); } - virtual CullResult cull(); - virtual void SendText(const std::string& line); - virtual const std::string& GetFullHost(); - virtual const std::string& GetFullRealHost(); + FakeUser(const std::string& uid, const std::string& sname, const std::string& sdesc) + : User(uid, new Server(sname, sdesc), USERTYPE_SERVER) + { + nick = sname; + } + + CullResult cull() CXX11_OVERRIDE; + const std::string& GetFullHost() CXX11_OVERRIDE; + const std::string& GetFullRealHost() CXX11_OVERRIDE; }; /* Faster than dynamic_cast */ @@ -926,42 +913,20 @@ inline FakeUser* IS_SERVER(User* u) { return u->usertype == USERTYPE_SERVER ? static_cast<FakeUser*>(u) : NULL; } -/** Is an oper */ -#define IS_OPER(x) (x->oper) -/** Is away */ -#define IS_AWAY(x) (!x->awaymsg.empty()) -/** Derived from Resolver, and performs user forward/reverse lookups. - */ -class CoreExport UserResolver : public Resolver +inline bool User::IsModeSet(const ModeHandler* mh) const { - private: - /** UUID we are looking up */ - std::string uuid; - /** True if the lookup is forward, false if is a reverse lookup - */ - bool fwd; - public: - /** Create a resolver. - * @param user The user to begin lookup on - * @param to_resolve The IP or host to resolve - * @param qt The query type - * @param cache Modified by the constructor if the result was cached - */ - UserResolver(LocalUser* user, std::string to_resolve, QueryType qt, bool &cache); - - /** Called on successful lookup - * @param result Result string - * @param ttl Time to live for result - * @param cached True if the result was found in the cache - */ - void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached); + return (modes[mh->GetId()]); +} - /** Called on failed lookup - * @param e Error code - * @param errormessage Error message string - */ - void OnError(ResolverError e, const std::string &errormessage); -}; +inline bool User::IsModeSet(UserModeReference& moderef) const +{ + if (!moderef) + return false; + return IsModeSet(*moderef); +} -#endif +inline void User::SetMode(ModeHandler* mh, bool value) +{ + modes[mh->GetId()] = value; +} diff --git a/include/xline.h b/include/xline.h index 2a49d8b80..7c102c882 100644 --- a/include/xline.h +++ b/include/xline.h @@ -20,8 +20,7 @@ */ -#ifndef XLINE_H -#define XLINE_H +#pragma once /** XLine is the base class for ban lines such as G lines and K lines. * Modules may derive from this, and their xlines will automatically be @@ -51,7 +50,12 @@ class CoreExport XLine : public classbase * @param t The line type, should be set by the derived class constructor */ XLine(time_t s_time, long d, std::string src, std::string re, const std::string &t) - : set_time(s_time), duration(d), source(src), reason(re), type(t) + : set_time(s_time) + , duration(d) + , source(src) + , reason(re) + , type(t) + , from_config(false) { expiry = set_time + duration; } @@ -101,16 +105,16 @@ class CoreExport XLine : public classbase * line. Usually a line in the form 'expiring Xline blah, set by...' * see the DisplayExpiry methods of GLine, ELine etc. */ - virtual void DisplayExpiry() = 0; + virtual void DisplayExpiry(); /** Returns the displayable form of the pattern for this xline, * e.g. '*\@foo' or '*baz*'. This must always return the full pattern * in a form which can be used to construct an entire derived xline, * even if it is stored differently internally (e.g. GLine stores the * ident and host parts seperately but will still return ident\@host - * for its Displayable() method) + * for its Displayable() method). */ - virtual const char* Displayable() = 0; + virtual const std::string& Displayable() = 0; /** Called when the xline has just been added. */ @@ -141,6 +145,9 @@ class CoreExport XLine : public classbase */ const std::string type; + // Whether this XLine was loaded from the server config. + bool from_config; + virtual bool IsBurstable(); }; @@ -171,17 +178,15 @@ class CoreExport KLine : public XLine { } - virtual bool Matches(User *u); + bool Matches(User* u) CXX11_OVERRIDE; - virtual bool Matches(const std::string &str); + bool Matches(const std::string& str) CXX11_OVERRIDE; - virtual void Apply(User* u); - - virtual void DisplayExpiry(); + void Apply(User* u) CXX11_OVERRIDE; - virtual const char* Displayable(); + const std::string& Displayable() CXX11_OVERRIDE; - virtual bool IsBurstable(); + bool IsBurstable() CXX11_OVERRIDE; /** Ident mask (ident part only) */ @@ -219,15 +224,13 @@ class CoreExport GLine : public XLine { } - virtual bool Matches(User *u); + bool Matches(User* u) CXX11_OVERRIDE; - virtual bool Matches(const std::string &str); + bool Matches(const std::string& str) CXX11_OVERRIDE; - virtual void Apply(User* u); + void Apply(User* u) CXX11_OVERRIDE; - virtual void DisplayExpiry(); - - virtual const char* Displayable(); + const std::string& Displayable() CXX11_OVERRIDE; /** Ident mask (ident part only) */ @@ -263,17 +266,15 @@ class CoreExport ELine : public XLine { } - virtual bool Matches(User *u); + bool Matches(User* u) CXX11_OVERRIDE; - virtual bool Matches(const std::string &str); + bool Matches(const std::string& str) CXX11_OVERRIDE; - virtual void Unset(); + void Unset() CXX11_OVERRIDE; - virtual void DisplayExpiry(); + void OnAdd() CXX11_OVERRIDE; - virtual void OnAdd(); - - virtual const char* Displayable(); + const std::string& Displayable() CXX11_OVERRIDE; /** Ident mask (ident part only) */ @@ -308,15 +309,13 @@ class CoreExport ZLine : public XLine { } - virtual bool Matches(User *u); + bool Matches(User* u) CXX11_OVERRIDE; - virtual bool Matches(const std::string &str); + bool Matches(const std::string& str) CXX11_OVERRIDE; - virtual void Apply(User* u); + void Apply(User* u) CXX11_OVERRIDE; - virtual void DisplayExpiry(); - - virtual const char* Displayable(); + const std::string& Displayable() CXX11_OVERRIDE; /** IP mask (no ident part) */ @@ -345,15 +344,13 @@ class CoreExport QLine : public XLine ~QLine() { } - virtual bool Matches(User *u); + bool Matches(User* u) CXX11_OVERRIDE; - virtual bool Matches(const std::string &str); + bool Matches(const std::string& str) CXX11_OVERRIDE; - virtual void Apply(User* u); + void Apply(User* u) CXX11_OVERRIDE; - virtual void DisplayExpiry(); - - virtual const char* Displayable(); + const std::string& Displayable() CXX11_OVERRIDE; /** Nickname mask */ @@ -531,10 +528,10 @@ class CoreExport XLineManager * will be expired and removed before the list is displayed. * @param type The type of stats to show * @param numeric The numeric to give to each result line - * @param user The username making the query - * @param results The string_list to receive the results + * @param stats Stats context */ - void InvokeStats(const std::string &type, int numeric, User* user, string_list &results); -}; + void InvokeStats(const std::string& type, unsigned int numeric, Stats::Context& stats); -#endif + /** Clears any XLines which were added by the server configuration. */ + void ClearConfigLines(); +}; |