2 * InspIRCd -- Internet Relay Chat Daemon
4 * Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com>
5 * Copyright (C) 2018-2020 Sadie Powell <sadie@witchery.services>
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/>.
22 #include "modules/cap.h"
23 #include "modules/ircv3_batch.h"
25 class LabeledResponseTag : public ClientProtocol::MessageTagProvider
28 const Cap::Capability& cap;
33 const std::string labeltag;
35 LabeledResponseTag(Module* mod, const Cap::Capability& capref)
36 : ClientProtocol::MessageTagProvider(mod)
43 ModResult OnProcessTag(User* user, const std::string& tagname, std::string& tagvalue) CXX11_OVERRIDE
45 if (!irc::equals(tagname, labeltag))
46 return MOD_RES_PASSTHRU;
48 // If the tag is empty or too long then we can't accept it.
49 if (tagvalue.empty() || tagvalue.size() > 64)
52 // If the user is local then we check whether they have the labeled-response
53 // cap enabled. If not then we reject the label tag originating from them.
54 LocalUser* lu = IS_LOCAL(user);
55 if (lu && !cap.get(lu))
58 // Remote users have their label tag checked by their local server.
62 bool ShouldSendTag(LocalUser* user, const ClientProtocol::MessageTagData& tagdata) CXX11_OVERRIDE
64 // Messages only have a label when being sent to a user that sent one.
65 return user == labeluser && tagdata.value == label;
69 class ModuleIRCv3LabeledResponse : public Module
73 LabeledResponseTag tag;
74 IRCv3::Batch::API batchmanager;
75 IRCv3::Batch::Batch batch;
76 IRCv3::Batch::CapReference batchcap;
77 ClientProtocol::EventProvider ackmsgprov;
78 ClientProtocol::EventProvider labelmsgprov;
79 insp::aligned_storage<ClientProtocol::Message> firstmsg;
82 void FlushFirstMsg(LocalUser* user)
84 // This isn't a side effect but we treat it like one to avoid the logic in OnUserWrite.
85 firstmsg->SetSideEffect(true);
86 user->Send(labelmsgprov, *firstmsg);
91 ModuleIRCv3LabeledResponse()
92 : cap(this, "labeled-response")
95 , batch("labeled-response")
97 , ackmsgprov(this, "ACK")
98 , labelmsgprov(this, "labeled")
104 ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated) CXX11_OVERRIDE
106 // We only care about the initial unvalidated OnPreCommand call.
107 if (validated || tag.labeluser)
108 return MOD_RES_PASSTHRU;
110 // We only care about registered users with the labeled-response and batch caps.
111 if (user->registered != REG_ALL || !cap.get(user) || !batchcap.get(user))
112 return MOD_RES_PASSTHRU;
114 const ClientProtocol::TagMap& tagmap = parameters.GetTags();
115 const ClientProtocol::TagMap::const_iterator labeltag = tagmap.find(tag.labeltag);
116 if (labeltag == tagmap.end())
117 return MOD_RES_PASSTHRU;
119 tag.label = labeltag->second.value;
120 tag.labeluser = user;
121 return MOD_RES_PASSTHRU;
124 void OnPostCommand(Command* command, const CommandBase::Params& parameters, LocalUser* user, CmdResult result, bool loop) CXX11_OVERRIDE
126 // Do nothing if this isn't the last OnPostCommand() run for the command.
128 // If a parameter for the command was originally a list and the command handler chose to be executed
129 // for each element on the list with synthesized parameters (CommandHandler::LoopCall) then this hook
130 // too will run for each element on the list plus once after the whole list has been processed.
131 // loop will only be false for the last run.
133 OnCommandBlocked(command->name, parameters, user);
136 void OnCommandBlocked(const std::string& command, const CommandBase::Params& parameters, LocalUser* user) CXX11_OVERRIDE
138 // If no label was sent we don't have to do anything.
146 // There was no response so we send an ACK instead.
147 ClientProtocol::Message ackmsg("ACK", ServerInstance->FakeClient);
148 ackmsg.AddTag(tag.labeltag, &tag, tag.label);
149 ackmsg.SetSideEffect(true);
150 tag.labeluser->Send(ackmsgprov, ackmsg);
156 // There was one response which was cached; send it now.
157 firstmsg->AddTag(tag.labeltag, &tag, tag.label);
164 // There was two or more responses; send an end-of-batch.
167 // Set end start as side effect so we'll ignore it otherwise it'd end up added into the batch.
168 batch.GetBatchEndMessage().SetSideEffect(true);
169 batchmanager->End(batch);
175 tag.labeluser = NULL;
179 ModResult OnUserWrite(LocalUser* user, ClientProtocol::Message& msg) CXX11_OVERRIDE
181 // The label user is writing a message to another user.
182 if (user != tag.labeluser)
183 return MOD_RES_PASSTHRU;
185 // The message is a side effect (e.g. a self-PRIVMSG).
186 if (msg.IsSideEffect())
187 return MOD_RES_PASSTHRU;
193 // First reply message. We can' send it yet because we don't know if there will be more.
194 new(firstmsg) ClientProtocol::Message(msg);
201 // Second reply message. This and all subsequent messages need to go into a batch.
204 batchmanager->Start(batch);
206 // Set batch start as side effect so we'll ignore it otherwise it'd end up added into the batch.
207 ClientProtocol::Message& batchstartmsg = batch.GetBatchStartMessage();
208 batchstartmsg.SetSideEffect(true);
209 batchstartmsg.AddTag(tag.labeltag, &tag, tag.label);
211 batch.AddToBatch(*firstmsg);
212 batch.AddToBatch(msg);
215 // Flush first message which triggers the batch start message
217 return MOD_RES_PASSTHRU;
222 // Third or later message. Put it in the batch and send directly.
224 batch.AddToBatch(msg);
225 return MOD_RES_PASSTHRU;
230 void Prioritize() CXX11_OVERRIDE
232 Module* alias = ServerInstance->Modules->Find("m_alias.so");
233 ServerInstance->Modules->SetPriority(this, I_OnPreCommand, PRIORITY_BEFORE, alias);
236 Version GetVersion() CXX11_OVERRIDE
238 return Version("Provides the labeled-response IRCv3 extension", VF_VENDOR);
242 MODULE_INIT(ModuleIRCv3LabeledResponse)