mirror of git://git.acid.vegas/unrealircd.git
407 lines
11 KiB
C
407 lines
11 KiB
C
/*
|
|
* Channel mode +D/+d: delayed join
|
|
* except from opers, U-lines and servers.
|
|
* Copyright 2014 Travis Mcarthur <Heero> and UnrealIRCd Team
|
|
*/
|
|
#include "unrealircd.h"
|
|
|
|
ModuleHeader MOD_HEADER
|
|
= {
|
|
"chanmodes/delayjoin", /* Name of module */
|
|
"5.0", /* Version */
|
|
"delayed join (+D,+d)", /* Short description of module */
|
|
"UnrealIRCd Team",
|
|
"unrealircd-6",
|
|
};
|
|
|
|
#define MOD_DATA_STR "delayjoin"
|
|
#define MOD_DATA_INVISIBLE "1"
|
|
|
|
static long UMODE_PRIVDEAF = 0;
|
|
static Cmode *CmodeDelayed = NULL;
|
|
static Cmode *CmodePostDelayed = NULL;
|
|
static Cmode_t EXTMODE_DELAYED;
|
|
static Cmode_t EXTMODE_POST_DELAYED;
|
|
|
|
int visible_in_channel(Client *client, Channel *channel);
|
|
int moded_check_part(Client *client, Channel *channel);
|
|
int moded_join(Client *client, Channel *channel);
|
|
int moded_part(Client *client, Channel *channel, MessageTag *mtags, const char *comment);
|
|
int moded_quit(Client *client, MessageTag *mtags, const char *comment);
|
|
int delayjoin_is_ok(Client *client, Channel *channel, char mode, const char *para, int checkt, int what);
|
|
int moded_chanmode(Client *client, Channel *channel,
|
|
MessageTag *mtags, const char *modebuf, const char *parabuf, time_t sendts, int samode);
|
|
int moded_prechanmsg(Client *client, Channel *channel, MessageTag *mtags, const char *text, SendType sendtype);
|
|
const char *moded_serialize(ModData *m);
|
|
void moded_unserialize(const char *str, ModData *m);
|
|
|
|
MOD_INIT()
|
|
{
|
|
CmodeInfo req;
|
|
ModDataInfo mreq;
|
|
|
|
MARK_AS_OFFICIAL_MODULE(modinfo);
|
|
ModuleSetOptions(modinfo->handle, MOD_OPT_PERM_RELOADABLE, 1);
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
req.paracount = 0;
|
|
req.is_ok = extcmode_default_requirechop;
|
|
req.letter = 'D';
|
|
CmodeDelayed = CmodeAdd(modinfo->handle, req, &EXTMODE_DELAYED);
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
req.paracount = 0;
|
|
req.is_ok = delayjoin_is_ok;
|
|
req.letter = 'd';
|
|
req.local = 1;
|
|
CmodePostDelayed = CmodeAdd(modinfo->handle, req, &EXTMODE_POST_DELAYED);
|
|
|
|
memset(&mreq, 0, sizeof(mreq));
|
|
mreq.name = MOD_DATA_STR;
|
|
mreq.serialize = moded_serialize;
|
|
mreq.unserialize = moded_unserialize;
|
|
mreq.sync = 0;
|
|
mreq.type = MODDATATYPE_MEMBER;
|
|
if (!ModDataAdd(modinfo->handle, mreq))
|
|
abort();
|
|
|
|
if (!CmodeDelayed || !CmodePostDelayed)
|
|
{
|
|
/* I use config_error() here because it's printed to stderr in case of a load
|
|
* on cmd line, and to all opers in case of a /rehash.
|
|
*/
|
|
config_error("delayjoin: Could not add channel mode '+D' or '+d': %s", ModuleGetErrorStr(modinfo->handle));
|
|
return MOD_FAILED;
|
|
}
|
|
|
|
HookAdd(modinfo->handle, HOOKTYPE_VISIBLE_IN_CHANNEL, 0, visible_in_channel);
|
|
HookAdd(modinfo->handle, HOOKTYPE_JOIN_DATA, 0, moded_join);
|
|
HookAdd(modinfo->handle, HOOKTYPE_LOCAL_PART, 0, moded_part);
|
|
HookAdd(modinfo->handle, HOOKTYPE_REMOTE_PART, 0, moded_part);
|
|
HookAdd(modinfo->handle, HOOKTYPE_LOCAL_QUIT, 0, moded_quit);
|
|
HookAdd(modinfo->handle, HOOKTYPE_REMOTE_QUIT, 0, moded_quit);
|
|
HookAdd(modinfo->handle, HOOKTYPE_PRE_LOCAL_CHANMODE, 0, moded_chanmode);
|
|
HookAdd(modinfo->handle, HOOKTYPE_PRE_REMOTE_CHANMODE, 0, moded_chanmode);
|
|
HookAdd(modinfo->handle, HOOKTYPE_PRE_CHANMSG, 0, moded_prechanmsg);
|
|
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
MOD_LOAD()
|
|
{
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
MOD_UNLOAD()
|
|
{
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
void set_post_delayed(Channel *channel)
|
|
{
|
|
MessageTag *mtags = NULL;
|
|
|
|
channel->mode.mode |= EXTMODE_POST_DELAYED;
|
|
|
|
new_message(&me, NULL, &mtags);
|
|
sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, mtags, ":%s MODE %s +d", me.name, channel->name);
|
|
free_message_tags(mtags);
|
|
}
|
|
|
|
void clear_post_delayed(Channel *channel)
|
|
{
|
|
MessageTag *mtags = NULL;
|
|
|
|
channel->mode.mode &= ~EXTMODE_POST_DELAYED;
|
|
|
|
new_message(&me, NULL, &mtags);
|
|
sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, mtags, ":%s MODE %s -d", me.name, channel->name);
|
|
free_message_tags(mtags);
|
|
}
|
|
|
|
bool moded_member_invisible(Member* m, Channel *channel)
|
|
{
|
|
ModDataInfo *md;
|
|
|
|
if (!m)
|
|
return false;
|
|
|
|
md = findmoddata_byname(MOD_DATA_STR, MODDATATYPE_MEMBER);
|
|
if (!md)
|
|
return false;
|
|
|
|
if (!moddata_member(m, md).str)
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
bool moded_user_invisible(Client *client, Channel *channel)
|
|
{
|
|
return moded_member_invisible(find_member_link(channel->members, client), channel);
|
|
}
|
|
|
|
bool channel_has_invisible_users(Channel *channel)
|
|
{
|
|
Member* i;
|
|
for (i = channel->members; i; i = i->next)
|
|
{
|
|
if (moded_member_invisible(i, channel))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool channel_is_post_delayed(Channel *channel)
|
|
{
|
|
if (channel->mode.mode & EXTMODE_POST_DELAYED)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
bool channel_is_delayed(Channel *channel)
|
|
{
|
|
if (channel->mode.mode & EXTMODE_DELAYED)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
void clear_user_invisible(Channel *channel, Client *client)
|
|
{
|
|
Member *i;
|
|
ModDataInfo *md;
|
|
bool should_clear = true, found_member = false;
|
|
|
|
md = findmoddata_byname(MOD_DATA_STR, MODDATATYPE_MEMBER);
|
|
if (!md)
|
|
return;
|
|
for (i = channel->members; i; i = i->next)
|
|
{
|
|
if (i->client == client)
|
|
{
|
|
|
|
if (md)
|
|
memset(&moddata_member(i, md), 0, sizeof(ModData));
|
|
|
|
found_member = true;
|
|
|
|
if (!should_clear)
|
|
break;
|
|
}
|
|
|
|
else if (moddata_member(i, md).str)
|
|
{
|
|
should_clear = false;
|
|
if (found_member)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (should_clear && (channel->mode.mode & EXTMODE_POST_DELAYED))
|
|
{
|
|
clear_post_delayed(channel);
|
|
}
|
|
}
|
|
|
|
void clear_user_invisible_announce(Channel *channel, Client *client, MessageTag *recv_mtags)
|
|
{
|
|
Member *i;
|
|
MessageTag *mtags = NULL;
|
|
char joinbuf[512];
|
|
char exjoinbuf[512];
|
|
long CAP_EXTENDED_JOIN = ClientCapabilityBit("extended-join");
|
|
|
|
clear_user_invisible(channel, client);
|
|
|
|
ircsnprintf(joinbuf, sizeof(joinbuf), ":%s!%s@%s JOIN %s",
|
|
client->name, client->user->username, GetHost(client), channel->name);
|
|
|
|
ircsnprintf(exjoinbuf, sizeof(exjoinbuf), ":%s!%s@%s JOIN %s %s :%s",
|
|
client->name, client->user->username, GetHost(client), channel->name,
|
|
IsLoggedIn(client) ? client->user->account : "*",
|
|
client->info);
|
|
|
|
new_message_special(client, recv_mtags, &mtags, ":%s JOIN %s", client->name, channel->name);
|
|
for (i = channel->members; i; i = i->next)
|
|
{
|
|
Client *acptr = i->client;
|
|
if (!check_channel_access(acptr, channel, "hoaq") && acptr != client && MyConnect(acptr))
|
|
{
|
|
if (HasCapabilityFast(acptr, CAP_EXTENDED_JOIN))
|
|
sendto_one(acptr, mtags, "%s", exjoinbuf);
|
|
else
|
|
sendto_one(acptr, mtags, "%s", joinbuf);
|
|
}
|
|
}
|
|
free_message_tags(mtags);
|
|
}
|
|
|
|
void set_user_invisible(Channel *channel, Client *client)
|
|
{
|
|
Member *m = find_member_link(channel->members, client);
|
|
ModDataInfo *md;
|
|
|
|
if (!m)
|
|
return;
|
|
|
|
md = findmoddata_byname(MOD_DATA_STR, MODDATATYPE_MEMBER);
|
|
|
|
if (!md || !md->unserialize)
|
|
return;
|
|
|
|
md->unserialize(MOD_DATA_INVISIBLE, &moddata_member(m, md));
|
|
}
|
|
|
|
|
|
int delayjoin_is_ok(Client *client, Channel *channel, char mode, const char *para, int checkt, int what)
|
|
{
|
|
return EX_ALWAYS_DENY;
|
|
}
|
|
|
|
|
|
int visible_in_channel(Client *client, Channel *channel)
|
|
{
|
|
return (channel_is_delayed(channel) || channel_is_post_delayed(channel)) && moded_user_invisible(client, channel);
|
|
}
|
|
|
|
|
|
int moded_join(Client *client, Channel *channel)
|
|
{
|
|
if (channel_is_delayed(channel))
|
|
set_user_invisible(channel, client);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int moded_part(Client *client, Channel *channel, MessageTag *mtags, const char *comment)
|
|
{
|
|
if (channel_is_delayed(channel) || channel_is_post_delayed(channel))
|
|
clear_user_invisible(channel, client);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int moded_quit(Client *client, MessageTag *mtags, const char *comment)
|
|
{
|
|
Membership *membership;
|
|
Channel *channel;
|
|
|
|
for (membership = client->user->channel; membership; membership=membership->next)
|
|
{
|
|
channel = membership->channel;
|
|
/* Identical to moded_part() */
|
|
if (channel_is_delayed(channel) || channel_is_post_delayed(channel))
|
|
clear_user_invisible(channel, client);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int moded_chanmode(Client *client, Channel *channel, MessageTag *recv_mtags, const char *modebuf, const char *parabuf, time_t sendts, int samode)
|
|
{
|
|
long CAP_EXTENDED_JOIN = ClientCapabilityBit("extended-join");
|
|
|
|
// Handle case where we just unset +D but have invisible users
|
|
if (!channel_is_delayed(channel) && !channel_is_post_delayed(channel) && channel_has_invisible_users(channel))
|
|
set_post_delayed(channel);
|
|
else if (channel_is_delayed(channel) && channel_is_post_delayed(channel))
|
|
clear_post_delayed(channel);
|
|
|
|
if ((channel_is_delayed(channel) || channel_is_post_delayed(channel)))
|
|
{
|
|
ParseMode pm;
|
|
int ret;
|
|
for (ret = parse_chanmode(&pm, modebuf, parabuf); ret; ret = parse_chanmode(&pm, NULL, NULL))
|
|
{
|
|
if (pm.what == MODE_ADD && (pm.modechar == 'o' || pm.modechar == 'h' || pm.modechar == 'a' || pm.modechar == 'q' || pm.modechar == 'v'))
|
|
{
|
|
Member* i;
|
|
Client *user = find_client(pm.param,NULL);
|
|
if (!user)
|
|
continue;
|
|
|
|
if (moded_user_invisible(user, channel))
|
|
clear_user_invisible_announce(channel, user, recv_mtags);
|
|
|
|
if (pm.modechar == 'v' || !MyConnect(user))
|
|
continue;
|
|
|
|
/* Our user 'user' just got ops (oaq) - send the joins for all the users (s)he doesn't know about */
|
|
for (i = channel->members; i; i = i->next)
|
|
{
|
|
if (i->client == user)
|
|
continue;
|
|
if (moded_user_invisible(i->client, channel))
|
|
{
|
|
MessageTag *mtags = NULL;
|
|
new_message_special(i->client, recv_mtags, &mtags, ":%s JOIN %s", i->client->name, channel->name);
|
|
if (HasCapabilityFast(user, CAP_EXTENDED_JOIN))
|
|
{
|
|
sendto_one(user, mtags, ":%s!%s@%s JOIN %s %s :%s",
|
|
i->client->name, i->client->user->username, GetHost(i->client),
|
|
channel->name,
|
|
IsLoggedIn(i->client) ? i->client->user->account : "*",
|
|
i->client->info);
|
|
} else {
|
|
sendto_one(user, mtags, ":%s!%s@%s JOIN :%s", i->client->name, i->client->user->username, GetHost(i->client), channel->name);
|
|
}
|
|
free_message_tags(mtags);
|
|
}
|
|
}
|
|
|
|
}
|
|
if (pm.what == MODE_DEL && (pm.modechar == 'o' || pm.modechar == 'h' || pm.modechar == 'a' || pm.modechar == 'q' || pm.modechar == 'v'))
|
|
{
|
|
Member* i;
|
|
Client *user = find_client(pm.param,NULL);
|
|
if (!user)
|
|
continue;
|
|
|
|
if (moded_user_invisible(user, channel))
|
|
clear_user_invisible_announce(channel, user, recv_mtags);
|
|
|
|
if (pm.modechar == 'v' || !MyConnect(user))
|
|
continue;
|
|
|
|
/* Our user 'user' just lost ops (oaq) - send the parts for all users (s)he won't see anymore */
|
|
for (i = channel->members; i; i = i->next)
|
|
{
|
|
if (i->client == user)
|
|
continue;
|
|
if (moded_user_invisible(i->client, channel))
|
|
{
|
|
MessageTag *mtags = NULL;
|
|
new_message_special(i->client, recv_mtags, &mtags, ":%s PART %s", i->client->name, channel->name);
|
|
sendto_one(user, mtags, ":%s!%s@%s PART :%s", i->client->name, i->client->user->username, GetHost(i->client), channel->name);
|
|
free_message_tags(mtags);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int moded_prechanmsg(Client *client, Channel *channel, MessageTag *mtags, const char *text, SendType sendtype)
|
|
{
|
|
if ((channel_is_delayed(channel) || channel_is_post_delayed(channel)) && (moded_user_invisible(client, channel)))
|
|
clear_user_invisible_announce(channel, client, mtags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
const char *moded_serialize(ModData *m)
|
|
{
|
|
return m->i ? "1" : "0";
|
|
}
|
|
|
|
void moded_unserialize(const char *str, ModData *m)
|
|
{
|
|
m->i = atoi(str);
|
|
}
|