summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
Diffstat (limited to 'include')
-rw-r--r--include/channels.h71
-rw-r--r--include/clientprotocol.h726
-rw-r--r--include/clientprotocolevent.h78
-rw-r--r--include/clientprotocolmsg.h677
-rw-r--r--include/command_parse.h2
-rw-r--r--include/configreader.h5
-rw-r--r--include/ctables.h35
-rw-r--r--include/event.h33
-rw-r--r--include/inspircd.h19
-rw-r--r--include/message.h9
-rw-r--r--include/mode.h12
-rw-r--r--include/modechange.h17
-rw-r--r--include/modules.h7
-rw-r--r--include/modules/cap.h19
-rw-r--r--include/modules/ircv3.h64
-rw-r--r--include/stdalgo.h82
-rw-r--r--include/typedefs.h28
-rw-r--r--include/users.h90
18 files changed, 1821 insertions, 153 deletions
diff --git a/include/channels.h b/include/channels.h
index 365cdeabd..0557a5898 100644
--- a/include/channels.h
+++ b/include/channels.h
@@ -232,75 +232,22 @@ class CoreExport Channel : public Extensible
*/
Membership* ForceJoin(User* user, const std::string* privs = NULL, bool bursting = false, bool created_by_local = false);
- /** 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
- */
- void WriteChannel(User* user, const std::string &text);
-
- /** 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
- */
- void WriteChannelWithServ(const std::string& ServName, const char* text, ...) CUSTOM_PRINTF(3, 4);
-
- /** 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);
+ 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,
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 f5cb47620..98484ca54 100644
--- a/include/command_parse.h
+++ b/include/command_parse.h
@@ -38,7 +38,7 @@ class CoreExport CommandParser
* @param command The name of the command.
* @param parameters The parameters to the command.
*/
- void ProcessCommand(LocalUser* user, std::string& command, Command::Params& parameters);
+ void ProcessCommand(LocalUser* user, std::string& command, CommandBase::Params& parameters);
/** Command list, a hash_map of command names to Command*
*/
diff --git a/include/configreader.h b/include/configreader.h
index 81ec014a0..4cb051eff 100644
--- a/include/configreader.h
+++ b/include/configreader.h
@@ -430,11 +430,6 @@ class CoreExport ServerConfig
*/
std::string CaseMapping;
- /** If set to true, the CycleHosts mode change will be sourced from the user,
- * rather than the server
- */
- bool CycleHostsFromUser;
-
/** 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.
*/
diff --git a/include/ctables.h b/include/ctables.h
index c34e4abeb..8be40cc54 100644
--- a/include/ctables.h
+++ b/include/ctables.h
@@ -110,7 +110,40 @@ struct RouteDescriptor
class CoreExport CommandBase : public ServiceProvider
{
public:
- typedef std::vector<std::string> Params;
+ /** 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
*/
diff --git a/include/event.h b/include/event.h
index c9bad7d04..73a45a541 100644
--- a/include/event.h
+++ b/include/event.h
@@ -34,7 +34,12 @@ namespace Events
class Events::ModuleEventProvider : public ServiceProvider, private dynamic_reference_base::CaptureHook
{
public:
- typedef std::vector<ModuleEventListener*> SubscriberList;
+ struct Comp
+ {
+ bool operator()(ModuleEventListener* one, ModuleEventListener* two) const;
+ };
+
+ typedef insp::flat_multiset<ModuleEventListener*, Comp> SubscriberList;
/** Constructor
* @param mod Module providing the event(s)
@@ -84,20 +89,25 @@ class Events::ModuleEventListener : private dynamic_reference_base::CaptureHook
*/
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.push_back(this);
+ 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)
+ 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
@@ -108,18 +118,25 @@ class Events::ModuleEventListener : private dynamic_reference_base::CaptureHook
~ModuleEventListener()
{
if (prov)
- stdalgo::erase(prov->subscribers, this);
+ 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) \
+ 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 ; \
@@ -135,8 +152,8 @@ class Events::ModuleEventListener : private dynamic_reference_base::CaptureHook
*/
#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) \
+ 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 ; \
diff --git a/include/inspircd.h b/include/inspircd.h
index 00093e52b..90ee6ca8d 100644
--- a/include/inspircd.h
+++ b/include/inspircd.h
@@ -91,6 +91,7 @@ struct fakederef
#include "filelogger.h"
#include "message.h"
#include "modules.h"
+#include "clientprotocol.h"
#include "threadengine.h"
#include "configreader.h"
#include "inspstring.h"
@@ -193,6 +194,8 @@ 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.
*/
@@ -565,6 +568,8 @@ class CoreExport InspIRCd
{
return this->ReadBuffer;
}
+
+ ClientProtocol::RFCEvents& GetRFCEvents() { return rfcevents; }
};
ENTRYPOINT;
@@ -590,4 +595,18 @@ inline void stdalgo::culldeleter::operator()(classbase* item)
ServerInstance->GlobalCulls.AddItem(item);
}
+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);
+}
+
+inline void LocalUser::Send(ClientProtocol::EventProvider& protoevprov, ClientProtocol::Message& msg)
+{
+ ClientProtocol::Event event(protoevprov, msg);
+ Send(event);
+}
+
#include "numericbuilder.h"
+#include "clientprotocolmsg.h"
+#include "clientprotocolevent.h"
diff --git a/include/message.h b/include/message.h
index fb9e7619f..1799e6119 100644
--- a/include/message.h
+++ b/include/message.h
@@ -41,15 +41,22 @@ class CoreExport MessageDetails
/* The original message as sent by the user. */
const std::string originaltext;
+ /** 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;
- MessageDetails(MessageType mt, const std::string& msg)
+ MessageDetails(MessageType mt, const std::string& msg, const ClientProtocol::TagMap& tags)
: echooriginal(false)
, originaltext(msg)
+ , tags_in(tags)
, text(msg)
, type(mt)
{
diff --git a/include/mode.h b/include/mode.h
index 4fa78bcac..ac23adc33 100644
--- a/include/mode.h
+++ b/include/mode.h
@@ -614,11 +614,6 @@ class CoreExport ModeParser : public fakederef<ModeParser>
*/
ModeHandler::Id AllocateModeId(ModeType mt);
- /** The string representing the last set of modes to be parsed.
- * Use GetLastParse() to get this value, to be used for display purposes.
- */
- std::string LastParse;
-
/** Cached mode list for use in 004 numeric
*/
TR1NS::array<std::string, 3> Cached004ModeList;
@@ -681,13 +676,6 @@ class CoreExport ModeParser : public fakederef<ModeParser>
/** Gets the last mode change to be processed. */
const Modes::ChangeList& GetLastChangeList() const { return LastChangeList; }
- /** 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 { return LastParse; }
-
/** Add a mode to the mode parser.
* Throws a ModuleException if the mode cannot be added.
*/
diff --git a/include/modechange.h b/include/modechange.h
index 885c22900..9ec105e73 100644
--- a/include/modechange.h
+++ b/include/modechange.h
@@ -54,6 +54,23 @@ class Modes::ChangeList
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
diff --git a/include/modules.h b/include/modules.h
index 4e5648512..ba84c4ccc 100644
--- a/include/modules.h
+++ b/include/modules.h
@@ -227,7 +227,7 @@ enum Implementation
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_OnServiceAdd, I_OnServiceDel, I_OnUserWrite,
I_END
};
@@ -561,9 +561,8 @@ class CoreExport Module : public classbase, public usecountbase
* @param changelist The changed modes.
* @param processflags Flags passed to ModeParser::Process(), see ModeParser::ModeProcessFlags
* for the possible flags.
- * @param output_mode Changed modes, including '+' and '-' characters, not including any parameters
*/
- virtual void OnMode(User* user, User* usertarget, Channel* chantarget, const Modes::ChangeList& changelist, ModeParser::ModeProcessFlag processflags, const std::string& output_mode);
+ 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.
@@ -953,6 +952,8 @@ class CoreExport Module : public classbase, public usecountbase
* @param service ServiceProvider being unregistered.
*/
virtual void OnServiceDel(ServiceProvider& service);
+
+ virtual ModResult OnUserWrite(LocalUser* user, ClientProtocol::Message& msg);
};
/** ModuleManager takes care of all things module-related
diff --git a/include/modules/cap.h b/include/modules/cap.h
index 8299d14ae..6dcb9f3bc 100644
--- a/include/modules/cap.h
+++ b/include/modules/cap.h
@@ -313,4 +313,23 @@ namespace Cap
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/ircv3.h b/include/modules/ircv3.h
index e03ee16fa..338abdeba 100644
--- a/include/modules/ircv3.h
+++ b/include/modules/ircv3.h
@@ -19,27 +19,83 @@
#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;
- const std::string& msg;
+ ClientProtocol::Event& protoev;
void Execute(LocalUser* user) CXX11_OVERRIDE
{
if (cap.get(user))
- user->Write(msg);
+ user->Send(protoev);
}
public:
- WriteNeighborsWithCap(User* user, const std::string& message, const Cap::Capability& capability)
+ WriteNeighborsWithCap(User* user, ClientProtocol::Event& ev, const Cap::Capability& capability)
: cap(capability)
- , msg(message)
+ , 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/stdalgo.h b/include/stdalgo.h
index bb5e12262..d69f50bb2 100644
--- a/include/stdalgo.h
+++ b/include/stdalgo.h
@@ -210,4 +210,86 @@ namespace stdalgo
{
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/typedefs.h b/include/typedefs.h
index 9a015d445..20fc596be 100644
--- a/include/typedefs.h
+++ b/include/typedefs.h
@@ -49,6 +49,34 @@ class XLineFactory;
struct ConnectClass;
struct ModResult;
+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"
diff --git a/include/users.h b/include/users.h
index 21a35645d..e8f5399e8 100644
--- a/include/users.h
+++ b/include/users.h
@@ -498,41 +498,10 @@ class CoreExport User : public Extensible
*/
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
- */
- virtual void Write(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
- */
- virtual void Write(const char *text, ...) CUSTOM_PRINTF(2, 3);
-
- /** 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);
-
- /** Sends a command to this user.
- * @param command The command to be sent.
- * @param text The message to send.
- */
- void WriteCommand(const char* command, const std::string& text);
-
/** Sends a server notice to this user.
* @param text The contents of the message to send.
*/
- void WriteNotice(const std::string& text) { this->WriteCommand("NOTICE", ":" + text); }
+ void WriteNotice(const std::string& text);
/** Send a NOTICE message from the local server to the user.
* @param text Text to send
@@ -643,30 +612,11 @@ class CoreExport User : public Extensible
WriteNumeric(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);
-
- /** 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);
-
/** 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 protoev Protocol event to send, may contain any number of messages.
* @param include_self Should the message be sent back to the author?
*/
- void WriteCommonRaw(const std::string &line, bool include_self = true);
-
- /** 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);
+ void WriteCommonRaw(ClientProtocol::Event& protoev, bool include_self = true);
/** 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.
@@ -750,12 +700,32 @@ typedef unsigned int already_sent_t;
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() CXX11_OVERRIDE;
UserIOHandler eh;
+ /** Serializer to use when communicating with the user
+ */
+ ClientProtocol::Serializer* serializer;
+
/** Stats counter for bytes inbound
*/
unsigned int bytes_in;
@@ -850,9 +820,6 @@ class CoreExport LocalUser : public User, public insp::intrusive_list_node<Local
void SetClientIP(const irc::sockets::sockaddrs& sa, bool recheck_eline = true) CXX11_OVERRIDE;
- void Write(const std::string& text) CXX11_OVERRIDE;
- void Write(const char*, ...) CXX11_OVERRIDE CUSTOM_PRINTF(2, 3);
-
/** 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
@@ -890,6 +857,17 @@ class CoreExport LocalUser : public User, public insp::intrusive_list_node<Local
* 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 RemoteUser : public User