2 * InspIRCd -- Internet Relay Chat Daemon
4 * Copyright (C) 2018-2019 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/>.
22 #include "modules/cap.h"
23 #include "modules/ircv3_batch.h"
25 class BatchMessage : public ClientProtocol::Message
28 BatchMessage(const IRCv3::Batch::Batch& batch, bool start)
29 : ClientProtocol::Message("BATCH", ServerInstance->Config->ServerName)
31 char c = (start ? '+' : '-');
32 PushParam(std::string(1, c) + batch.GetRefTagStr());
33 if ((start) && (!batch.GetType().empty()))
34 PushParamRef(batch.GetType());
38 /** Extra structure allocated only for running batches, containing objects only relevant for
39 * that specific run of the batch.
41 struct IRCv3::Batch::BatchInfo
43 /** List of users that have received the batch start message
45 std::vector<LocalUser*> users;
46 BatchMessage startmsg;
47 ClientProtocol::Event startevent;
49 ClientProtocol::Event endevent;
51 BatchInfo(ClientProtocol::EventProvider& protoevprov, IRCv3::Batch::Batch& b)
53 , startevent(protoevprov, startmsg)
55 , endevent(protoevprov, endmsg)
60 class IRCv3::Batch::ManagerImpl : public Manager
62 typedef std::vector<Batch*> BatchList;
65 ClientProtocol::EventProvider protoevprov;
66 LocalIntExt batchbits;
67 BatchList active_batches;
70 bool ShouldSendTag(LocalUser* user, const ClientProtocol::MessageTagData& tagdata) CXX11_OVERRIDE
75 Batch& batch = *static_cast<Batch*>(tagdata.provdata);
76 // Check if this is the first message the user is getting that is part of the batch
77 const intptr_t bits = batchbits.get(user);
78 if (!(bits & batch.GetBit()))
80 // Send the start batch command ("BATCH +reftag TYPE"), remember the user so we can send them a
81 // "BATCH -reftag" message later when the batch ends and set the flag we just checked so this is
82 // only done once per user per batch.
83 batchbits.set(user, (bits | batch.GetBit()));
84 batch.batchinfo->users.push_back(user);
85 user->Send(batch.batchinfo->startevent);
91 unsigned int NextFreeId() const
93 if (active_batches.empty())
95 return active_batches.back()->GetId()+1;
99 ManagerImpl(Module* mod)
102 , protoevprov(mod, "BATCH")
103 , batchbits("batchbits", ExtensionItem::EXT_USER, mod)
110 // Set batchbits to 0 for all users in case we were reloaded and the previous, now meaningless,
111 // batchbits are set on users
112 const UserManager::LocalList& users = ServerInstance->Users.GetLocalUsers();
113 for (UserManager::LocalList::const_iterator i = users.begin(); i != users.end(); ++i)
115 LocalUser* const user = *i;
116 batchbits.set(user, 0);
123 while (!active_batches.empty())
124 ManagerImpl::End(*active_batches.back());
127 void RemoveFromAll(LocalUser* user)
129 const intptr_t bits = batchbits.get(user);
131 // User is quitting, remove them from all lists
132 for (BatchList::iterator i = active_batches.begin(); i != active_batches.end(); ++i)
135 // Check the bit first to avoid list scan in case they're not on the list
136 if ((bits & batch.GetBit()) != 0)
137 stdalgo::vector::swaperase(batch.batchinfo->users, user);
141 void Start(Batch& batch) CXX11_OVERRIDE
146 if (batch.IsRunning())
147 return; // Already started, don't start again
149 const size_t id = NextFreeId();
150 if (id >= MAX_BATCHES)
154 // Set the manager field which Batch::IsRunning() checks and is also used by AddToBatch()
155 // to set the message tag
156 batch.manager = this;
157 batch.batchinfo = new IRCv3::Batch::BatchInfo(protoevprov, batch);
158 batch.batchstartmsg = &batch.batchinfo->startmsg;
159 batch.batchendmsg = &batch.batchinfo->endmsg;
160 active_batches.push_back(&batch);
163 void End(Batch& batch) CXX11_OVERRIDE
165 if (!batch.IsRunning())
168 // Mark batch as stopped
169 batch.manager = NULL;
171 BatchInfo& batchinfo = *batch.batchinfo;
172 // Send end batch message to all users who got the batch start message and unset bit so it can be reused
173 for (std::vector<LocalUser*>::const_iterator i = batchinfo.users.begin(); i != batchinfo.users.end(); ++i)
175 LocalUser* const user = *i;
176 user->Send(batchinfo.endevent);
177 batchbits.set(user, batchbits.get(user) & ~batch.GetBit());
180 // erase() not swaperase because the reftag generation logic depends on the order of the elements
181 stdalgo::erase(active_batches, &batch);
182 delete batch.batchinfo;
183 batch.batchinfo = NULL;
187 class ModuleIRCv3Batch : public Module
189 IRCv3::Batch::ManagerImpl manager;
197 void init() CXX11_OVERRIDE
202 void OnUnloadModule(Module* mod) CXX11_OVERRIDE
208 void OnUserDisconnect(LocalUser* user) CXX11_OVERRIDE
210 // Remove the user from all internal lists
211 manager.RemoveFromAll(user);
214 Version GetVersion() CXX11_OVERRIDE
216 return Version("Provides the IRCv3 batch client capability.", VF_VENDOR);
220 MODULE_INIT(ModuleIRCv3Batch)