mirror of
https://github.com/matrix-construct/construct
synced 2024-11-17 23:40:57 +01:00
416 lines
8.6 KiB
C++
416 lines
8.6 KiB
C++
/*
|
|
* Copyright (C) 2004-2005 Lee Hardy <lee -at- leeh.co.uk>
|
|
* Copyright (C) 2004-2005 ircd-ratbox development team
|
|
* Copyright (C) 2016 Charybdis Development Team
|
|
* Copyright (C) 2016 Jason Volk <jason@zemos.net>
|
|
*
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice is present in all copies.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
|
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#pragma once
|
|
#define HAVE_IRCD_HOOK_H
|
|
|
|
namespace ircd {
|
|
namespace hook {
|
|
|
|
using ctx::future;
|
|
|
|
// Function returns an empty future for serialization; valid for asynchronous
|
|
template<class state> using closure = std::function<future<> (state)>;
|
|
|
|
// Gives name of event required to happen.first (before), and required to happen.second (after)
|
|
using relationship = std::pair<std::string, std::string>;
|
|
|
|
// Returns true if A happens before B (for sorting)
|
|
bool happens_before(const std::string &a_name, const relationship &a_happens,
|
|
const std::string &b_name, const relationship &b_happens);
|
|
|
|
|
|
template<class state = void>
|
|
struct phase
|
|
{
|
|
std::string name;
|
|
relationship happens;
|
|
closure<state> function;
|
|
|
|
phase(const std::string &name, const relationship &, closure<state>&&);
|
|
phase(const std::string &name, closure<state>&&);
|
|
phase() = default;
|
|
};
|
|
|
|
template<class state>
|
|
phase<state>::phase(const std::string &name,
|
|
closure<state>&& function)
|
|
:phase{name, {}, std::forward<closure<state>>(function)}
|
|
{
|
|
}
|
|
|
|
template<class state>
|
|
phase<state>::phase(const std::string &name,
|
|
const relationship &happens,
|
|
closure<state>&& function)
|
|
:name{name}
|
|
,happens{happens}
|
|
,function{std::forward<closure<state>>(function)}
|
|
{
|
|
}
|
|
|
|
|
|
template<class state>
|
|
struct execution
|
|
{
|
|
std::map<std::string, future<>> pending; // phase.name => future
|
|
std::multimap<std::string, std::string> fences; // happens.second = > phase name
|
|
|
|
void fences_wait(const std::string &name);
|
|
void pending_wait(const std::string &name);
|
|
void pending_wait();
|
|
};
|
|
|
|
template<class state>
|
|
void
|
|
execution<state>::pending_wait()
|
|
{
|
|
for(const auto &pit : pending)
|
|
{
|
|
const auto &future(pit.second);
|
|
future.wait();
|
|
}
|
|
}
|
|
|
|
template<class state>
|
|
void
|
|
execution<state>::pending_wait(const std::string &name)
|
|
{
|
|
const auto pit(pending.find(name));
|
|
if(pit == end(pending))
|
|
return;
|
|
|
|
const scope erase([this, &pit]
|
|
{
|
|
pending.erase(pit);
|
|
});
|
|
|
|
const auto &future(pit->second);
|
|
future.wait();
|
|
}
|
|
|
|
template<class state>
|
|
void
|
|
execution<state>::fences_wait(const std::string &name)
|
|
{
|
|
const auto ppit(fences.equal_range(name));
|
|
const scope erase([this, &ppit]
|
|
{
|
|
fences.erase(ppit.first, ppit.second);
|
|
});
|
|
|
|
for(auto pit(ppit.first); pit != ppit.second; ++pit)
|
|
{
|
|
const auto &name(pit->second);
|
|
pending_wait(name);
|
|
}
|
|
}
|
|
|
|
|
|
template<class state = void>
|
|
struct sequence
|
|
{
|
|
std::list<phase<state>> space;
|
|
|
|
void sort();
|
|
|
|
public:
|
|
template<class... phase> void add(phase&&...);
|
|
void del(const std::string &name);
|
|
|
|
void operator()(state);
|
|
};
|
|
|
|
template<class state>
|
|
void
|
|
sequence<state>::operator()(state s)
|
|
{
|
|
execution<state> e;
|
|
for(const auto &phase : space)
|
|
{
|
|
e.fences_wait(phase.name);
|
|
e.pending_wait(phase.happens.first);
|
|
|
|
auto future(phase.function(s));
|
|
if(!future.valid())
|
|
continue;
|
|
|
|
if(!phase.happens.second.empty())
|
|
e.fences.emplace(phase.happens.second, phase.name);
|
|
|
|
e.pending.emplace(phase.name, std::move(future));
|
|
}
|
|
|
|
e.pending_wait();
|
|
}
|
|
|
|
template<class state>
|
|
void
|
|
sequence<state>::del(const std::string &name)
|
|
{
|
|
space.remove_if([&name]
|
|
(const auto &phase)
|
|
{
|
|
return phase.name == name;
|
|
});
|
|
}
|
|
|
|
template<class state>
|
|
template<class... phase>
|
|
void
|
|
sequence<state>::add(phase&&... p)
|
|
{
|
|
space.emplace_back(std::forward<phase>(p)...);
|
|
sort();
|
|
}
|
|
|
|
template<class state>
|
|
void
|
|
sequence<state>::sort()
|
|
{
|
|
space.sort([]
|
|
(const auto &a, const auto &b)
|
|
{
|
|
return happens_before(a.name, a.happens, b.name, b.happens);
|
|
});
|
|
}
|
|
|
|
|
|
template<class state = void>
|
|
class lambda
|
|
{
|
|
sequence<state> *s;
|
|
std::string name;
|
|
|
|
public:
|
|
lambda(sequence<state> &, const std::string &name, const relationship &, closure<state>);
|
|
lambda(sequence<state> &, const std::string &name, closure<state>);
|
|
~lambda() noexcept;
|
|
};
|
|
|
|
template<class state>
|
|
lambda<state>::lambda(sequence<state> &s,
|
|
const std::string &name,
|
|
const relationship &relationship,
|
|
closure<state> closure)
|
|
:s{&s}
|
|
,name{name}
|
|
{
|
|
s.add(name, relationship, std::move(closure));
|
|
}
|
|
|
|
template<class state>
|
|
lambda<state>::lambda(sequence<state> &s,
|
|
const std::string &name,
|
|
closure<state> closure)
|
|
:lambda{s, name, {}, std::move(closure)}
|
|
{
|
|
}
|
|
|
|
template<class state>
|
|
lambda<state>::~lambda()
|
|
noexcept
|
|
{
|
|
s->del(name);
|
|
}
|
|
|
|
|
|
template<class state = void>
|
|
class function
|
|
:lambda<state>
|
|
{
|
|
virtual future<> operator()(state) const = 0;
|
|
|
|
public:
|
|
function(sequence<state> &, const std::string &name, const relationship & = {});
|
|
virtual ~function() noexcept = default;
|
|
};
|
|
|
|
template<class state>
|
|
function<state>::function(sequence<state> &s,
|
|
const std::string &name,
|
|
const relationship &relationship)
|
|
:lambda<state>{s, name, relationship, [this]
|
|
(state&& st)
|
|
{
|
|
return this->operator()(std::forward<state>(st));
|
|
}}
|
|
{
|
|
}
|
|
|
|
} // namespace hook
|
|
} // namespace ircd
|
|
|
|
|
|
/*
|
|
#ifdef __cplusplus
|
|
namespace ircd {
|
|
|
|
typedef struct
|
|
{
|
|
char *name;
|
|
rb_dlink_list hooks;
|
|
} hook;
|
|
|
|
typedef void (*hookfn) (void *data);
|
|
|
|
extern int h_iosend_id;
|
|
extern int h_iorecv_id;
|
|
extern int h_iorecvctrl_id;
|
|
|
|
extern int h_burst_client;
|
|
extern int h_burst_channel;
|
|
extern int h_burst_finished;
|
|
extern int h_server_introduced;
|
|
extern int h_server_eob;
|
|
extern int h_client_exit;
|
|
extern int h_umode_changed;
|
|
extern int h_new_local_user;
|
|
extern int h_new_remote_user;
|
|
extern int h_introduce_client;
|
|
extern int h_can_kick;
|
|
extern int h_privmsg_channel;
|
|
extern int h_privmsg_user;
|
|
extern int h_conf_read_start;
|
|
extern int h_conf_read_end;
|
|
extern int h_outbound_msgbuf;
|
|
extern int h_rehash;
|
|
|
|
void init_hook(void);
|
|
int register_hook(const char *name);
|
|
void add_hook(const char *name, hookfn fn);
|
|
void remove_hook(const char *name, hookfn fn);
|
|
void call_hook(int id, void *arg);
|
|
|
|
typedef struct
|
|
{
|
|
client::client *client;
|
|
void *arg1;
|
|
void *arg2;
|
|
} hook_data;
|
|
|
|
typedef struct
|
|
{
|
|
client::client *client;
|
|
const void *arg1;
|
|
const void *arg2;
|
|
} hook_cdata;
|
|
|
|
typedef struct
|
|
{
|
|
client::client *client;
|
|
const void *arg1;
|
|
int arg2;
|
|
int result;
|
|
} hook_data_int;
|
|
|
|
typedef struct
|
|
{
|
|
client::client *client;
|
|
client::client *target;
|
|
chan::chan *chptr;
|
|
int approved;
|
|
} hook_data_client;
|
|
|
|
typedef struct
|
|
{
|
|
client::client *client;
|
|
chan::chan *chptr;
|
|
int approved;
|
|
} hook_data_channel;
|
|
|
|
typedef struct
|
|
{
|
|
client::client *client;
|
|
chan::chan *chptr;
|
|
char *key;
|
|
} hook_data_channel_activity;
|
|
|
|
typedef struct
|
|
{
|
|
client::client *client;
|
|
chan::chan *chptr;
|
|
chan::membership *msptr;
|
|
client::client *target;
|
|
int approved;
|
|
int dir;
|
|
const char *modestr;
|
|
} hook_data_channel_approval;
|
|
|
|
typedef struct
|
|
{
|
|
client::client *client;
|
|
client::client *target;
|
|
int approved;
|
|
} hook_data_client_approval;
|
|
|
|
typedef struct
|
|
{
|
|
client::client *local_link; // local client originating this, or NULL
|
|
client::client *target; // dying client
|
|
client::client *from; // causing client (could be &me or target)
|
|
const char *comment;
|
|
} hook_data_client_exit;
|
|
|
|
typedef struct
|
|
{
|
|
client::client *client;
|
|
unsigned int oldumodes;
|
|
unsigned int oldsnomask;
|
|
} hook_data_umode_changed;
|
|
|
|
enum message_type {
|
|
MESSAGE_TYPE_NOTICE,
|
|
MESSAGE_TYPE_PRIVMSG,
|
|
MESSAGE_TYPE_PART,
|
|
MESSAGE_TYPE_COUNT
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
enum message_type msgtype;
|
|
client::client *source_p;
|
|
chan::chan *chptr;
|
|
const char *text;
|
|
int approved;
|
|
const char *reason;
|
|
} hook_data_privmsg_channel;
|
|
|
|
typedef struct
|
|
{
|
|
enum message_type msgtype;
|
|
client::client *source_p;
|
|
client::client *target_p;
|
|
const char *text;
|
|
int approved;
|
|
} hook_data_privmsg_user;
|
|
|
|
typedef struct
|
|
{
|
|
bool signal;
|
|
} hook_data_rehash;
|
|
|
|
} // namespace ircd
|
|
#endif // __cplusplus
|
|
|
|
*/
|