2 * InspIRCd -- Internet Relay Chat Daemon
4 * Copyright (C) 2018-2020 Sadie Powell <sadie@witchery.services>
5 * Copyright (C) 2018 Attila Molnar <attilamolnar@hush.com>
7 * This file is part of InspIRCd. InspIRCd is free software: you can
8 * redistribute it and/or modify it under the terms of the GNU General Public
9 * License as published by the Free Software Foundation, version 2.
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 namespace ClientProtocol
34 /** Contains a message parsed from wire format.
35 * Used by Serializer::Parse().
37 struct ClientProtocol::ParseOutput
39 /** Command name, must not be empty.
43 /** Parameter list, may be empty.
45 ClientProtocol::ParamList params;
47 /** Message tags, may be empty.
49 ClientProtocol::TagMap tags;
52 /** A selection of zero or more tags in a TagMap.
54 class ClientProtocol::TagSelection
56 std::bitset<64> selection;
59 /** Check if a tag is selected.
60 * @param tags TagMap the tag is in. The TagMap must contain the same tags as it had when the tag
61 * was selected with Select(), otherwise the result is not meaningful.
62 * @param it Iterator to the tag to check.
63 * @return True if the tag is selected, false otherwise.
65 bool IsSelected(const TagMap& tags, TagMap::const_iterator it) const
67 const size_t index = std::distance(tags.begin(), it);
68 return ((index < selection.size()) && (selection[index]));
72 * @param tags TagMap the tag is in. This parameter must be the same every time the method is called.
73 * The TagMap must not be altered otherwise the results of IsSelected() is not meaningful.
74 * @param it Iterator to the tag to mark as selected.
76 void Select(const TagMap& tags, TagMap::const_iterator it)
78 const size_t index = std::distance(tags.begin(), it);
79 if (index < selection.size())
80 selection[index] = true;
83 /** Check if a TagSelection is equivalent to this object.
84 * @param other Other TagSelection object to compare this with.
85 * @return True if the objects are equivalent, false if they aren't.
87 bool operator==(const TagSelection& other) const
89 return (this->selection == other.selection);
93 class ClientProtocol::MessageSource
96 const std::string* sourcestr;
99 /** Constructor, sets the source to be the full host of a user or sets it to be nothing.
100 * The actual source string when serializing will be obtained from User::GetFullHost() if the user is non-NULL.
101 * @param Sourceuser User to set as source of the message. If NULL, the message won't have a source when serialized.
102 * Optional, defaults to NULL.
104 MessageSource(User* Sourceuser = NULL)
106 SetSourceUser(Sourceuser);
109 /** Constructor, sets the source to the supplied string and optionally sets the source user.
110 * @param Sourcestr String to use as message source for serialization purposes. Must remain valid
111 * as long as this object is alive.
112 * @param Sourceuser User to set as source. Optional, defaults to NULL. It will not be used for serialization but
113 * if provided it may be used internally, for example to create message tags.
114 * Useful when the source string is synthesized but it is still related to a User.
116 MessageSource(const std::string& Sourcestr, User* Sourceuser = NULL)
118 SetSource(Sourcestr, Sourceuser);
121 /** Get the source of this message as a string.
122 * @return Pointer to the message source string or NULL if there is no source.
124 const std::string* GetSource() const
126 // Return string if there's one explicitly set
130 return &sourceuser->GetFullHost();
134 /** Get the source User.
135 * This shouldn't be used for serialization, use GetSource() for that.
136 * @return User pointer if the message has a source user, NULL otherwise.
138 User* GetSourceUser() const { return sourceuser; }
140 /** Set the source of this message to a User.
141 * See the one parameter constructor for a more detailed description.
142 * @param Sourceuser User to set as source.
144 void SetSourceUser(User* Sourceuser)
146 sourceuser = Sourceuser;
150 /** Set the source string and optionally source user.
151 * See the two parameter constructor for a more detailed description.
152 * @param Sourcestr String source, to be used for serialization purposes. Must remain valid as long
153 * as this object is alive.
154 * @param Sourceuser Source user to set, optional.
156 void SetSource(const std::string& Sourcestr, User* Sourceuser = NULL)
158 sourcestr = &Sourcestr;
159 sourceuser = Sourceuser;
162 /** Copy the source from a MessageSource object.
163 * @param other MessageSource object to copy from.
165 void SetSource(const MessageSource& other)
167 sourcestr = other.sourcestr;
168 sourceuser = other.sourceuser;
172 /** Outgoing client protocol message.
173 * Represents a high level client protocol message which is turned into raw or wire format
174 * by a Serializer. Messages can be serialized into different format by different serializers.
176 * Messages are created on demand and are disposed of after they have been sent.
178 * All messages have a command name, a list of parameters and a map of tags, the last two can be empty.
179 * They also always have a source, see class MessageSource.
181 class ClientProtocol::Message : public ClientProtocol::MessageSource
184 /** Contains information required to identify a specific version of a serialized message.
186 struct SerializedInfo
188 const Serializer* serializer;
192 * @param Ser Serializer used to serialize the message.
193 * @param Tagwl Tag whitelist used to serialize the message.
195 SerializedInfo(const Serializer* Ser, const TagSelection& Tagwl)
201 /** Check if a SerializedInfo object is equivalent to this object.
202 * @param other Other SerializedInfo object.
203 * @return True if other is equivalent to this object, false otherwise.
205 bool operator==(const SerializedInfo& other) const
207 return ((serializer == other.serializer) && (tagwl == other.tagwl));
213 const std::string* ptr;
214 insp::aligned_storage<std::string> str;
217 void InitFrom(const Param& other)
221 new(str) std::string(*other.str);
227 operator const std::string&() const { return (owned ? *str : *ptr); }
235 Param(const std::string& s)
241 Param(int, const char* s)
245 new(str) std::string(s);
248 Param(int, const std::string& s)
252 new(str) std::string(s);
255 Param(const Param& other)
267 Param& operator=(const Param& other)
279 bool IsOwned() const { return owned; }
282 typedef std::vector<Param> ParamList;
285 typedef std::vector<std::pair<SerializedInfo, SerializedMessage> > SerializedList;
291 mutable SerializedList serlist;
295 /** Set command string.
296 * @param cmd Command string to set.
298 void SetCommand(const char* cmd)
307 * @param cmd Command name, e.g. "JOIN", "NICK". May be NULL. If NULL, the command must be set
308 * with SetCommand() before the message is serialized.
309 * @param Sourceuser See the one parameter constructor of MessageSource for description.
311 Message(const char* cmd, User* Sourceuser = NULL)
312 : ClientProtocol::MessageSource(Sourceuser)
313 , command(cmd ? cmd : std::string())
314 , msginit_done(false)
322 * @param cmd Command name, e.g. "JOIN", "NICK". May be NULL. If NULL, the command must be set
323 * with SetCommand() before the message is serialized.
324 * @param Sourcestr See the two parameter constructor of MessageSource for description.
325 * Must remain valid as long as this object is alive.
326 * @param Sourceuser See the two parameter constructor of MessageSource for description.
328 Message(const char* cmd, const std::string& Sourcestr, User* Sourceuser = NULL)
329 : ClientProtocol::MessageSource(Sourcestr, Sourceuser)
330 , command(cmd ? cmd : std::string())
331 , msginit_done(false)
338 /** Get the parameters of this message.
339 * @return List of parameters.
341 const ParamList& GetParams() const { return params; }
343 /** Get a map of tags attached to this message.
344 * The map contains the tag providers that attached the tag to the message.
345 * @return Map of tags.
347 const TagMap& GetTags() const { return tags; }
349 /** Get the command string.
350 * @return Command string, e.g. "NICK", "001".
352 const char* GetCommand() const { return command.c_str(); }
354 /** Add a parameter to the parameter list.
355 * @param str String to add, will be copied.
357 void PushParam(const char* str) { params.push_back(Param(0, str)); }
359 /** Add a parameter to the parameter list.
360 * @param str String to add, will be copied.
362 void PushParam(const std::string& str) { params.push_back(Param(0, str)); }
364 /** Add a parameter to the parameter list.
365 * @param str String to add.
366 * The string will NOT be copied, it must remain alive until ClearParams() is called or until the object is destroyed.
368 void PushParamRef(const std::string& str) { params.push_back(str); }
370 /** Add a placeholder parameter to the parameter list.
371 * Placeholder parameters must be filled in later with actual parameters using ReplaceParam() or ReplaceParamRef().
373 void PushParamPlaceholder() { params.push_back(Param()); }
375 /** Replace a parameter or a placeholder that is already in the parameter list.
376 * @param index Index of the parameter to replace. Must be less than GetParams().size().
377 * @param str String to replace the parameter or placeholder with, will be copied.
379 void ReplaceParam(unsigned int index, const char* str) { params[index] = Param(0, str); }
381 /** Replace a parameter or a placeholder that is already in the parameter list.
382 * @param index Index of the parameter to replace. Must be less than GetParams().size().
383 * @param str String to replace the parameter or placeholder with, will be copied.
385 void ReplaceParam(unsigned int index, const std::string& str) { params[index] = Param(0, str); }
387 /** Replace a parameter or a placeholder that is already in the parameter list.
388 * @param index Index of the parameter to replace. Must be less than GetParams().size().
389 * @param str String to replace the parameter or placeholder with.
390 * The string will NOT be copied, it must remain alive until ClearParams() is called or until the object is destroyed.
392 void ReplaceParamRef(unsigned int index, const std::string& str) { params[index] = Param(str); }
395 * @param tagname Raw name of the tag to use in the protocol.
396 * @param tagprov Provider of the tag.
397 * @param val Tag value. If empty no value will be sent with the tag.
398 * @param tagdata Tag provider specific data, will be passed to MessageTagProvider::ShouldSendTag(). Optional, defaults to NULL.
400 void AddTag(const std::string& tagname, MessageTagProvider* tagprov, const std::string& val, void* tagdata = NULL)
402 tags.insert(std::make_pair(tagname, MessageTagData(tagprov, val, tagdata)));
405 /** Add all tags in a TagMap to the tags in this message. Existing tags will not be overwritten.
406 * @param newtags New tags to add.
408 void AddTags(const ClientProtocol::TagMap& newtags)
410 tags.insert(newtags.begin(), newtags.end());
413 /** Get the message in a serialized form.
414 * @param serializeinfo Information about which exact serialized form of the message is the caller asking for
415 * (which serializer to use and which tags to include).
416 * @return Serialized message according to serializeinfo. The returned reference remains valid until the
417 * next call to this method.
419 const SerializedMessage& GetSerialized(const SerializedInfo& serializeinfo) const;
421 /** Clear the parameter list and tags.
425 msginit_done = false;
431 /** Remove all serialized messages.
432 * If a parameter is changed after the message has been sent at least once, this method must be called before
433 * serializing the message again to ensure the cache won't contain stale data.
435 void InvalidateCache()
443 for (ParamList::iterator i = params.begin(); i != params.end(); ++i, j++)
447 ReplaceParam(j, curr);
451 void SetSideEffect(bool Sideeffect) { sideeffect = Sideeffect; }
452 bool IsSideEffect() const { return sideeffect; }
454 friend class Serializer;
457 /** Client protocol event class.
458 * All messages sent to a user must be part of an event. A single event may result in more than one protocol message
459 * being sent, for example a join event may result in a JOIN and a MODE protocol message sent to members of the channel
460 * if the joining user has some prefix modes set.
462 * Event hooks attached to a specific event can alter the messages sent for that event.
464 class ClientProtocol::Event
466 EventProvider* event;
468 const MessageList* initialmsglist;
473 * @param protoeventprov Protocol event provider the event is an instance of.
475 Event(EventProvider& protoeventprov)
476 : event(&protoeventprov)
478 , initialmsglist(NULL)
479 , eventinit_done(false)
484 * @param protoeventprov Protocol event provider the event is an instance of.
485 * @param msg Message to include in this event by default.
487 Event(EventProvider& protoeventprov, ClientProtocol::Message& msg)
488 : event(&protoeventprov)
490 , initialmsglist(NULL)
491 , eventinit_done(false)
495 /** Set a single message as the initial message in the event.
496 * Modules may alter this later.
498 void SetMessage(Message* msg)
501 initialmsglist = NULL;
504 /** Set a list of messages as the initial messages in the event.
505 * Modules may alter this later.
507 void SetMessageList(const MessageList& msglist)
510 initialmsglist = &msglist;
513 /** Get a list of messages to send to a user.
514 * The exact messages sent to a user are determined by the initial message(s) set and hooks.
515 * @param user User to get the messages for.
516 * @param messagelist List to fill in with messages to send to the user for the event
518 void GetMessagesForUser(LocalUser* user, MessageList& messagelist);
521 class ClientProtocol::MessageTagEvent
522 : public Events::ModuleEventProvider
525 MessageTagEvent(Module* mod)
526 : ModuleEventProvider(mod, "event/messagetag")
531 /** Base class for message tag providers.
532 * All message tags belong to a message tag provider. Message tag providers can populate messages
533 * with tags before the message is sent and they have the job of determining whether a user should
534 * get a message tag or be allowed to send one.
536 class ClientProtocol::MessageTagProvider : public Events::ModuleEventListener
540 * @param mod Module owning the provider.
542 MessageTagProvider(Module* mod)
543 : Events::ModuleEventListener(mod, "event/messagetag")
547 /** Called when a message is ready to be sent to give the tag provider a chance to add tags to the message.
548 * To add tags call Message::AddTag(). If the provided tag or tags have been added already elsewhere or if the
549 * provider doesn't want its tag(s) to be on the message, the implementation doesn't have to do anything special.
550 * The default implementation does nothing.
551 * @param msg Message to be populated with tags.
553 virtual void OnPopulateTags(ClientProtocol::Message& msg)
557 /** Called for each tag that the server receives from a client in a message.
558 * @param user User that sent the tag.
559 * @param tagname Name of the tag.
560 * @param tagvalue Value of the tag, empty string if the tag has no value. May be modified.
561 * @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,
562 * MOD_RES_PASSTHRU to make no decision. If no hooks accept a tag, the tag is rejected.
563 * The default implementation returns MOD_RES_PASSTHRU.
565 virtual ModResult OnProcessTag(User* user, const std::string& tagname, std::string& tagvalue)
567 return MOD_RES_PASSTHRU;
570 /** Called whenever a user is about to receive a message that has a tag attached which is provided by this provider
571 * to determine whether or not the user should get the tag.
572 * @param user User in question.
573 * @param tagdata Tag in question.
574 * @return True if the tag should be sent to the user, false otherwise.
576 virtual bool ShouldSendTag(LocalUser* user, const MessageTagData& tagdata) = 0;
579 /** Base class for client protocol event hooks.
580 * A protocol event hook is attached to a single event type. It has the ability to alter or block messages
581 * sent to users which belong to the event the hook is attached to.
583 class ClientProtocol::EventHook : public Events::ModuleEventListener
586 static std::string GetEventName(const std::string& name)
588 return "event/protoevent_" + name;
592 * @param mod Owner of the hook.
593 * @param name Name of the event to hook.
594 * @param priority Priority of the hook. Determines the order in which hooks for the same event get called.
597 EventHook(Module* mod, const std::string& name, unsigned int priority = Events::ModuleEventListener::DefaultPriority)
598 : Events::ModuleEventListener(mod, GetEventName(name), priority)
602 /** Called exactly once before an event is sent to any user.
603 * The default implementation doesn't do anything.
604 * @param ev Event being sent to one or more users.
606 virtual void OnEventInit(const ClientProtocol::Event& ev)
610 /** Called for each user that may receive the event.
611 * The hook may alter the messages sent to the user and decide whether further hooks should be executed.
612 * @param user User the message list is being prepared to be sent to.
613 * @param ev Event associated with the messages.
614 * @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,
615 * add new messages, etc. The list must not be empty when the method returns.
616 * @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.
617 * MOD_RES_DENY to not send any messages to the user and to not run other hooks,
618 * MOD_RES_ALLOW to send the messages in messagelist to the user and to not run other hooks.
620 virtual ModResult OnPreEventSend(LocalUser* user, const ClientProtocol::Event& ev, ClientProtocol::MessageList& messagelist) = 0;
623 /** Event provider for client protocol events.
624 * Protocol event hooks can be attached to the instances of these providers. The core has event
625 * providers for most common IRC events defined in RFC1459.
627 class ClientProtocol::EventProvider : public Events::ModuleEventProvider
631 * @param Mod Module that owns the event provider.
632 * @param eventname Name of the event this provider is for, e.g. "JOIN", "PART", "NUMERIC".
633 * Should match command name if applicable.
635 EventProvider(Module* Mod, const std::string& eventname)
636 : Events::ModuleEventProvider(Mod, ClientProtocol::EventHook::GetEventName(eventname))
641 /** Commonly used client protocol events.
642 * Available via InspIRCd::GetRFCEvents().
644 struct ClientProtocol::RFCEvents
646 EventProvider numeric;
654 EventProvider privmsg;
655 EventProvider invite;
661 : numeric(NULL, "NUMERIC")
668 , topic(NULL, "TOPIC")
669 , privmsg(NULL, "PRIVMSG")
670 , invite(NULL, "INVITE")
673 , error(NULL, "ERROR")
678 /** Base class for client protocol serializers.
679 * A serializer has to implement serialization and parsing of protocol messages to/from wire format.
681 class CoreExport ClientProtocol::Serializer : public DataProvider
684 ClientProtocol::MessageTagEvent evprov;
686 /** Make a white list containing which tags a user should get.
687 * @param user User in question.
688 * @param tagmap Tag map that contains all possible tags.
689 * @return Whitelist of tags to send to the user.
691 TagSelection MakeTagWhitelist(LocalUser* user, const TagMap& tagmap) const;
695 * @param mod Module owning the serializer.
696 * @param Name Name of the serializer, e.g. "rfc".
698 Serializer(Module* mod, const char* Name);
700 /** Handle a tag in a message being parsed. Call this method for each parsed tag.
701 * @param user User sending the tag.
702 * @param tagname Name of the tag.
703 * @param tagvalue Tag value, may be empty.
704 * @param tags TagMap to place the tag into, if it gets accepted.
705 * @return True if no error occurred, false if the tag name is invalid or if this tag already exists.
707 bool HandleTag(LocalUser* user, const std::string& tagname, std::string& tagvalue, TagMap& tags) const;
709 /** Serialize a message for a user.
710 * @param user User to serialize the message for.
711 * @param msg Message to serialize.
712 * @return Raw serialized message, only containing the appropriate tags for the user.
713 * The reference is guaranteed to be valid as long as the Message object is alive and until the same
714 * Message is serialized for another user.
716 const SerializedMessage& SerializeForUser(LocalUser* user, Message& msg);
718 /** Serialize a high level protocol message into wire format.
719 * @param msg High level message to serialize. Contains all necessary information about the message, including all possible tags.
720 * @param tagwl Message tags to include in the serialized message. Tags attached to the message but not included in the whitelist must not
721 * appear in the output. This is because each user may get a different set of tags for the same message.
722 * @return Protocol message in wire format. Must contain message delimiter as well, if any (e.g. CRLF for RFC1459).
724 virtual std::string Serialize(const Message& msg, const TagSelection& tagwl) const = 0;
726 /** Parse a protocol message from wire format.
727 * @param user Source of the message.
728 * @param line Raw protocol message.
729 * @param parseoutput Output of the parser.
730 * @return True if the message was parsed successfully into parseoutput and should be processed, false to drop the message.
732 virtual bool Parse(LocalUser* user, const std::string& line, ParseOutput& parseoutput) = 0;
735 inline ClientProtocol::MessageTagData::MessageTagData(MessageTagProvider* prov, const std::string& val, void* data)