inspircd/include/modules/ircv3_batch.h
2024-06-07 10:37:56 +01:00

197 lines
6.1 KiB
C++

/*
* InspIRCd -- Internet Relay Chat Daemon
*
* Copyright (C) 2018-2022 Sadie Powell <sadie@witchery.services>
* Copyright (C) 2018 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
#include "modules/cap.h"
namespace IRCv3
{
namespace Batch
{
typedef uint64_t RefTag;
class Manager;
class ManagerImpl;
class Batch;
struct BatchInfo;
class API;
class CapReference;
static constexpr unsigned int MAX_BATCHES = (sizeof(intptr_t) * 8) - 1;
}
}
/** Batch Manager.
* Implements batch starting and stopping. When it becomes unavailable (due to e.g. module unload)
* all running batches are stopped.
*/
class IRCv3::Batch::Manager
: public DataProvider
, public ClientProtocol::MessageTagProvider
{
public:
/** Constructor.
* @param mod Module that owns the Manager.
*/
Manager(Module* mod)
: DataProvider(mod, "batchapi")
, ClientProtocol::MessageTagProvider(mod)
{
}
/** Start a batch.
* Check Batch::IsRunning() to learn if the batch has been started.
* @param batch Batch to start.
*/
virtual void Start(Batch& batch) = 0;
/** End a batch.
* @param batch Batch to end.
*/
virtual void End(Batch& batch) = 0;
};
/** Represents a batch.
* Batches are used to group together physically separate client protocol messages that logically belong
* together for one reason or another. The type of a batch, if provided, indicates what kind of grouping
* it does.
*
* Batch objects have two states: running and stopped. If a batch is running, messages can be added to it.
* If a message has been added to a batch and that message is sent to a client that negotiated the batch
* capability then the client will receive a message tag attached to the message indicating the batch that
* the message is a part of. If a message M is part of a batch B and M is sent to a client that hasn't yet
* received any message from batch B it will get a batch start message for B before M. When a batch B is
* stopped, every client that received at least one message which was in batch B will receive an end of
* batch message for B.
* A message may only be part of a single batch at any given time.
*/
class IRCv3::Batch::Batch final
{
Manager* manager = nullptr;
const std::string type;
RefTag reftag;
std::string reftagstr;
unsigned int bit;
BatchInfo* batchinfo = nullptr;
ClientProtocol::Message* batchstartmsg = nullptr;
ClientProtocol::Message* batchendmsg = nullptr;
void Setup(unsigned int b)
{
bit = b;
reftag = (static_cast<RefTag>(1) << bit);
reftagstr = ConvToStr(reftag);
}
unsigned int GetId() const { return bit; }
intptr_t GetBit() const { return reftag; }
public:
/** Constructor.
* The batch is initially stopped. To start it, pass it to Manager::Start().
* @param Type Batch type string, used to indicate what kind of grouping the batch does. May be empty.
*/
Batch(const std::string& Type)
: type(Type)
{
}
/** Destructor.
* If the batch is running, it is ended.
*/
~Batch()
{
if (manager)
manager->End(*this);
}
/** Add a message to the batch.
* If the batch isn't running then this method does nothing.
* @param msg Message to add to the batch. If it is already part of any batch, this method is a no-op.
*/
void AddToBatch(ClientProtocol::Message& msg)
{
if (manager)
msg.AddTag("batch", manager, reftagstr, this);
}
/** Get batch reference tag which is an opaque id for the batch and is used in the client protocol.
* Only running batches have a reference tag assigned.
* @return Reference tag as a string, only valid if the batch is running.
*/
const std::string& GetRefTagStr() const { return reftagstr; }
/** Get batch type.
* @return Batch type string.
*/
const std::string& GetType() const { return type; }
/** Check whether the batch is running.
* Batches can be started with Manager::Start() and stopped with Manager::End().
* @return True if the batch is running, false otherwise.
*/
bool IsRunning() const { return (manager != nullptr); }
/** Get the batch start client protocol message.
* The returned message object can be manipulated to add extra parameters or labels to the message. The first
* parameter of the message is the batch reference tag generated by the module providing batch support.
* If the batch type string was specified, it will be the second parameter of the message.
* May only be called if IsRunning() == true.
* @return Mutable batch start client protocol message.
*/
ClientProtocol::Message& GetBatchStartMessage() { return *batchstartmsg; }
/** Get the batch end client protocol message.
* The returned message object can be manipulated to add extra parameters or labels to the message. The first
* parameter of the message is the batch reference tag generated by the module providing batch support.
* If the batch type string was specified, it will be the second parameter of the message.
* May only be called if IsRunning() == true.
* @return Mutable batch end client protocol message.
*/
ClientProtocol::Message& GetBatchEndMessage() { return *batchendmsg; }
friend class ManagerImpl;
};
/** Batch API. Use this to access the Manager.
*/
class IRCv3::Batch::API final
: public dynamic_reference_nocheck<Manager>
{
public:
API(Module* mod)
: dynamic_reference_nocheck<Manager>(mod, "batchapi")
{
}
};
/** Reference to the batch cap.
* Can be used to check whether a user has the batch client cap enabled.
*/
class IRCv3::Batch::CapReference final
: public Cap::Reference
{
public:
CapReference(Module* mod)
: Cap::Reference(mod, "batch")
{
}
};