summaryrefslogtreecommitdiff
path: root/include/clientprotocolmsg.h
diff options
context:
space:
mode:
authorPeter Powell <petpow@saberuk.com>2018-08-13 20:17:46 +0100
committerPeter Powell <petpow@saberuk.com>2018-08-13 21:51:11 +0100
commit58a0a7e01422e62de1565a8eb0a1febdc463d04d (patch)
tree8861789deefe9df3524690de8ccd11e5366f1f2e /include/clientprotocolmsg.h
parente2a820cce21342478653a34cf8ce2b593128d035 (diff)
Implement IRCv3 message tag support.
Co-authored-by: Attila Molnar <attilamolnar@hush.com>
Diffstat (limited to 'include/clientprotocolmsg.h')
-rw-r--r--include/clientprotocolmsg.h677
1 files changed, 677 insertions, 0 deletions
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);
+ }
+};