]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_ircv3_batch.cpp
Add whether the server split with an error to OnServerSplit.
[user/henk/code/inspircd.git] / src / modules / m_ircv3_batch.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com>
5  *
6  * This file is part of InspIRCd.  InspIRCd is free software: you can
7  * redistribute it and/or modify it under the terms of the GNU General Public
8  * License as published by the Free Software Foundation, version 2.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
13  * details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19
20 #include "inspircd.h"
21 #include "modules/cap.h"
22 #include "modules/ircv3_batch.h"
23
24 class BatchMessage : public ClientProtocol::Message
25 {
26  public:
27         BatchMessage(const IRCv3::Batch::Batch& batch, bool start)
28                 : ClientProtocol::Message("BATCH", ServerInstance->Config->ServerName)
29         {
30                 char c = (start ? '+' : '-');
31                 PushParam(std::string(1, c) + batch.GetRefTagStr());
32                 if ((start) && (!batch.GetType().empty()))
33                         PushParamRef(batch.GetType());
34         }
35 };
36
37 /** Extra structure allocated only for running batches, containing objects only relevant for
38  * that specific run of the batch.
39  */
40 struct IRCv3::Batch::BatchInfo
41 {
42         /** List of users that have received the batch start message
43          */
44         std::vector<LocalUser*> users;
45         BatchMessage startmsg;
46         ClientProtocol::Event startevent;
47         BatchMessage endmsg;
48         ClientProtocol::Event endevent;
49
50         BatchInfo(ClientProtocol::EventProvider& protoevprov, IRCv3::Batch::Batch& b)
51                 : startmsg(b, true)
52                 , startevent(protoevprov, startmsg)
53                 , endmsg(b, false)
54                 , endevent(protoevprov, endmsg)
55         {
56         }
57 };
58
59 class IRCv3::Batch::ManagerImpl : public Manager
60 {
61         typedef std::vector<Batch*> BatchList;
62
63         Cap::Capability cap;
64         ClientProtocol::EventProvider protoevprov;
65         LocalIntExt batchbits;
66         BatchList active_batches;
67         bool unloading;
68
69         bool ShouldSendTag(LocalUser* user, const ClientProtocol::MessageTagData& tagdata) CXX11_OVERRIDE
70         {
71                 if (!cap.get(user))
72                         return false;
73
74                 Batch& batch = *static_cast<Batch*>(tagdata.provdata);
75                 // Check if this is the first message the user is getting that is part of the batch
76                 const intptr_t bits = batchbits.get(user);
77                 if (!(bits & batch.GetBit()))
78                 {
79                         // Send the start batch command ("BATCH +reftag TYPE"), remember the user so we can send them a
80                         // "BATCH -reftag" message later when the batch ends and set the flag we just checked so this is
81                         // only done once per user per batch.
82                         batchbits.set(user, (bits | batch.GetBit()));
83                         batch.batchinfo->users.push_back(user);
84                         user->Send(batch.batchinfo->startevent);
85                 }
86
87                 return true;
88         }
89
90         unsigned int NextFreeId() const
91         {
92                 if (active_batches.empty())
93                         return 0;
94                 return active_batches.back()->GetId()+1;
95         }
96
97  public:
98         ManagerImpl(Module* mod)
99                 : Manager(mod)
100                 , cap(mod, "batch")
101                 , protoevprov(mod, "BATCH")
102                 , batchbits("batchbits", ExtensionItem::EXT_USER, mod)
103                 , unloading(false)
104         {
105         }
106
107         void Init()
108         {
109                 // Set batchbits to 0 for all users in case we were reloaded and the previous, now meaningless,
110                 // batchbits are set on users
111                 const UserManager::LocalList& users = ServerInstance->Users.GetLocalUsers();
112                 for (UserManager::LocalList::const_iterator i = users.begin(); i != users.end(); ++i)
113                 {
114                         LocalUser* const user = *i;
115                         batchbits.set(user, 0);
116                 }
117         }
118
119         void Shutdown()
120         {
121                 unloading = true;
122                 while (!active_batches.empty())
123                         ManagerImpl::End(*active_batches.back());
124         }
125
126         void RemoveFromAll(LocalUser* user)
127         {
128                 const intptr_t bits = batchbits.get(user);
129
130                 // User is quitting, remove them from all lists
131                 for (BatchList::iterator i = active_batches.begin(); i != active_batches.end(); ++i)
132                 {
133                         Batch& batch = **i;
134                         // Check the bit first to avoid list scan in case they're not on the list
135                         if ((bits & batch.GetBit()) != 0)
136                                 stdalgo::vector::swaperase(batch.batchinfo->users, user);
137                 }
138         }
139
140         void Start(Batch& batch) CXX11_OVERRIDE
141         {
142                 if (unloading)
143                         return;
144
145                 if (batch.IsRunning())
146                         return; // Already started, don't start again
147
148                 const size_t id = NextFreeId();
149                 if (id >= MAX_BATCHES)
150                         return;
151
152                 batch.Setup(id);
153                 // Set the manager field which Batch::IsRunning() checks and is also used by AddToBatch()
154                 // to set the message tag
155                 batch.manager = this;
156                 batch.batchinfo = new IRCv3::Batch::BatchInfo(protoevprov, batch);
157                 batch.batchstartmsg = &batch.batchinfo->startmsg;
158                 batch.batchendmsg = &batch.batchinfo->endmsg;
159                 active_batches.push_back(&batch);
160         }
161
162         void End(Batch& batch) CXX11_OVERRIDE
163         {
164                 if (!batch.IsRunning())
165                         return;
166
167                 // Mark batch as stopped
168                 batch.manager = NULL;
169
170                 BatchInfo& batchinfo = *batch.batchinfo;
171                 // Send end batch message to all users who got the batch start message and unset bit so it can be reused
172                 for (std::vector<LocalUser*>::const_iterator i = batchinfo.users.begin(); i != batchinfo.users.end(); ++i)
173                 {
174                         LocalUser* const user = *i;
175                         user->Send(batchinfo.endevent);
176                         batchbits.set(user, batchbits.get(user) & ~batch.GetBit());
177                 }
178
179                 // erase() not swaperase because the reftag generation logic depends on the order of the elements
180                 stdalgo::erase(active_batches, &batch);
181                 delete batch.batchinfo;
182                 batch.batchinfo = NULL;
183         }
184 };
185
186 class ModuleIRCv3Batch : public Module
187 {
188         IRCv3::Batch::ManagerImpl manager;
189
190  public:
191         ModuleIRCv3Batch()
192                 : manager(this)
193         {
194         }
195
196         void init() CXX11_OVERRIDE
197         {
198                 manager.Init();
199         }
200
201         void OnUnloadModule(Module* mod) CXX11_OVERRIDE
202         {
203                 if (mod == this)
204                         manager.Shutdown();
205         }
206
207         void OnUserDisconnect(LocalUser* user) CXX11_OVERRIDE
208         {
209                 // Remove the user from all internal lists
210                 manager.RemoveFromAll(user);
211         }
212
213         Version GetVersion() CXX11_OVERRIDE
214         {
215                 return Version("Provides the batch IRCv3 extension", VF_VENDOR);
216         }
217 };
218
219 MODULE_INIT(ModuleIRCv3Batch)