unrealircd

- supernets unrealircd source & configuration
git clone git://git.acid.vegas/unrealircd.git
Log | Files | Refs | Archive

delayjoin.c (11367B)

      1 /*
      2  * Channel mode +D/+d: delayed join
      3  * except from opers, U-lines and servers.
      4  * Copyright 2014 Travis Mcarthur <Heero> and UnrealIRCd Team
      5  */
      6 #include "unrealircd.h"
      7 
      8 ModuleHeader MOD_HEADER
      9   = {
     10 	"chanmodes/delayjoin",   /* Name of module */
     11 	"5.0", /* Version */
     12 	"delayed join (+D,+d)", /* Short description of module */
     13 	"UnrealIRCd Team",
     14 	"unrealircd-6",
     15     };
     16 
     17 #define MOD_DATA_STR "delayjoin"
     18 #define MOD_DATA_INVISIBLE "1"
     19 
     20 static long UMODE_PRIVDEAF = 0;
     21 static Cmode *CmodeDelayed = NULL;
     22 static Cmode *CmodePostDelayed = NULL;
     23 static Cmode_t EXTMODE_DELAYED;
     24 static Cmode_t EXTMODE_POST_DELAYED;
     25 
     26 int visible_in_channel(Client *client, Channel *channel, Member *client_member);
     27 int moded_check_part(Client *client, Channel *channel);
     28 int moded_join(Client *client, Channel *channel);
     29 int moded_part(Client *client, Channel *channel, MessageTag *mtags, const char *comment);
     30 int moded_quit(Client *client, MessageTag *mtags, const char *comment);
     31 int delayjoin_is_ok(Client *client, Channel *channel, char mode, const char *para, int checkt, int what);
     32 int moded_chanmode(Client *client, Channel *channel,
     33                    MessageTag *mtags, const char *modebuf, const char *parabuf, time_t sendts, int samode);
     34 int moded_prechanmsg(Client *client, Channel *channel, MessageTag **mtags, const char *text, SendType sendtype);
     35 const char *moded_serialize(ModData *m);
     36 void moded_unserialize(const char *str, ModData *m);
     37 
     38 MOD_INIT()
     39 {
     40 	CmodeInfo req;
     41 	ModDataInfo mreq;
     42 
     43 	MARK_AS_OFFICIAL_MODULE(modinfo);
     44 	ModuleSetOptions(modinfo->handle, MOD_OPT_PERM_RELOADABLE, 1);
     45 
     46 	memset(&req, 0, sizeof(req));
     47 	req.paracount = 0;
     48 	req.is_ok = extcmode_default_requirechop;
     49 	req.letter = 'D';
     50 	CmodeDelayed = CmodeAdd(modinfo->handle, req, &EXTMODE_DELAYED);
     51 
     52 	memset(&req, 0, sizeof(req));
     53 	req.paracount = 0;
     54 	req.is_ok = delayjoin_is_ok;
     55 	req.letter = 'd';
     56 	req.local = 1;
     57 	CmodePostDelayed = CmodeAdd(modinfo->handle, req, &EXTMODE_POST_DELAYED);
     58 
     59 	memset(&mreq, 0, sizeof(mreq));
     60 	mreq.name = MOD_DATA_STR;
     61 	mreq.serialize = moded_serialize;
     62 	mreq.unserialize = moded_unserialize;
     63 	mreq.sync = 0;
     64 	mreq.type = MODDATATYPE_MEMBER;
     65 	if (!ModDataAdd(modinfo->handle, mreq))
     66 		abort();
     67 
     68 	if (!CmodeDelayed || !CmodePostDelayed)
     69 	{
     70 		/* I use config_error() here because it's printed to stderr in case of a load
     71 		 * on cmd line, and to all opers in case of a /rehash.
     72 		 */
     73 		config_error("delayjoin: Could not add channel mode '+D' or '+d': %s", ModuleGetErrorStr(modinfo->handle));
     74 		return MOD_FAILED;
     75 	}
     76 
     77 	HookAdd(modinfo->handle, HOOKTYPE_VISIBLE_IN_CHANNEL, 0, visible_in_channel);
     78 	HookAdd(modinfo->handle, HOOKTYPE_JOIN_DATA, 0, moded_join);
     79 	HookAdd(modinfo->handle, HOOKTYPE_LOCAL_PART, 0, moded_part);
     80 	HookAdd(modinfo->handle, HOOKTYPE_REMOTE_PART, 0, moded_part);
     81 	HookAdd(modinfo->handle, HOOKTYPE_LOCAL_QUIT, 0, moded_quit);
     82 	HookAdd(modinfo->handle, HOOKTYPE_REMOTE_QUIT, 0, moded_quit);
     83 	HookAdd(modinfo->handle, HOOKTYPE_PRE_LOCAL_CHANMODE, 0, moded_chanmode);
     84 	HookAdd(modinfo->handle, HOOKTYPE_PRE_REMOTE_CHANMODE, 0, moded_chanmode);
     85 	HookAdd(modinfo->handle, HOOKTYPE_PRE_CHANMSG, 0, moded_prechanmsg);
     86 
     87 	return MOD_SUCCESS;
     88 }
     89 
     90 MOD_LOAD()
     91 {
     92 	return MOD_SUCCESS;
     93 }
     94 
     95 MOD_UNLOAD()
     96 {
     97 	return MOD_SUCCESS;
     98 }
     99 
    100 void set_post_delayed(Channel *channel)
    101 {
    102 	MessageTag *mtags = NULL;
    103 
    104 	channel->mode.mode |= EXTMODE_POST_DELAYED;
    105 
    106 	new_message(&me, NULL, &mtags);
    107 	sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, mtags, ":%s MODE %s +d", me.name, channel->name);
    108 	free_message_tags(mtags);
    109 }
    110 
    111 void clear_post_delayed(Channel *channel)
    112 {
    113 	MessageTag *mtags = NULL;
    114 
    115 	channel->mode.mode &= ~EXTMODE_POST_DELAYED;
    116 
    117 	new_message(&me, NULL, &mtags);
    118 	sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, mtags, ":%s MODE %s -d", me.name, channel->name);
    119 	free_message_tags(mtags);
    120 }
    121 
    122 bool moded_member_invisible(Member* m, Channel *channel)
    123 {
    124 	ModDataInfo *md;
    125 
    126 	if (!m)
    127 		return false;
    128 
    129 	md = findmoddata_byname(MOD_DATA_STR, MODDATATYPE_MEMBER);
    130 	if (!md)
    131 		return false;
    132 
    133 	if (!moddata_member(m, md).str)
    134 		return false;
    135 
    136 	return true;
    137 
    138 }
    139 
    140 bool moded_user_invisible(Client *client, Channel *channel)
    141 {
    142 	return moded_member_invisible(find_member_link(channel->members, client), channel);
    143 }
    144 
    145 bool channel_has_invisible_users(Channel *channel)
    146 {
    147 	Member* i;
    148 	for (i = channel->members; i; i = i->next)
    149 	{
    150 		if (moded_member_invisible(i, channel))
    151 		{
    152 			return true;
    153 		}
    154 	}
    155 	return false;
    156 }
    157 
    158 bool channel_is_post_delayed(Channel *channel)
    159 {
    160 	if (channel->mode.mode & EXTMODE_POST_DELAYED)
    161 		return true;
    162 	return false;
    163 }
    164 
    165 bool channel_is_delayed(Channel *channel)
    166 {
    167 	if (channel->mode.mode & EXTMODE_DELAYED)
    168 		return true;
    169 	return false;
    170 }
    171 
    172 void clear_user_invisible(Channel *channel, Client *client)
    173 {
    174 	Member *i;
    175 	ModDataInfo *md;
    176 	bool should_clear = true, found_member = false;
    177 
    178 	md = findmoddata_byname(MOD_DATA_STR, MODDATATYPE_MEMBER);
    179 	if (!md)
    180 		return;
    181 	for (i = channel->members; i; i = i->next)
    182 	{
    183 		if (i->client == client)
    184 		{
    185 
    186 			if (md)
    187 				memset(&moddata_member(i, md), 0, sizeof(ModData));
    188 
    189 			found_member = true;
    190 
    191 			if (!should_clear)
    192 				break;
    193 		}
    194 
    195 		else if (moddata_member(i, md).str)
    196 		{
    197 			should_clear = false;
    198 			if (found_member)
    199 				break;
    200 		}
    201 	}
    202 
    203 	if (should_clear && (channel->mode.mode & EXTMODE_POST_DELAYED))
    204 	{
    205 		clear_post_delayed(channel);
    206 	}
    207 }
    208 
    209 void clear_user_invisible_announce(Channel *channel, Client *client, MessageTag *recv_mtags)
    210 {
    211 	Member *i;
    212 	MessageTag *mtags = NULL;
    213 	char joinbuf[512];
    214 	char exjoinbuf[512];
    215 	long CAP_EXTENDED_JOIN = ClientCapabilityBit("extended-join");
    216 
    217 	clear_user_invisible(channel, client);
    218 
    219 	ircsnprintf(joinbuf, sizeof(joinbuf), ":%s!%s@%s JOIN %s",
    220 				client->name, client->user->username, GetHost(client), channel->name);
    221 
    222 	ircsnprintf(exjoinbuf, sizeof(exjoinbuf), ":%s!%s@%s JOIN %s %s :%s",
    223 		client->name, client->user->username, GetHost(client), channel->name,
    224 		IsLoggedIn(client) ? client->user->account : "*",
    225 		client->info);
    226 
    227 	new_message_special(client, recv_mtags, &mtags, ":%s JOIN %s", client->name, channel->name);
    228 	for (i = channel->members; i; i = i->next)
    229 	{
    230 		Client *acptr = i->client;
    231 		if (!check_channel_access(acptr, channel, "hoaq") && acptr != client && MyConnect(acptr))
    232 		{
    233 			if (HasCapabilityFast(acptr, CAP_EXTENDED_JOIN))
    234 				sendto_one(acptr, mtags, "%s", exjoinbuf);
    235 			else
    236 				sendto_one(acptr, mtags, "%s", joinbuf);
    237 		}
    238 	}
    239 	free_message_tags(mtags);
    240 }
    241 
    242 void set_user_invisible(Channel *channel, Client *client)
    243 {
    244 	Member *m = find_member_link(channel->members, client);
    245 	ModDataInfo *md;
    246 
    247 	if (!m)
    248 		return;
    249 
    250 	md = findmoddata_byname(MOD_DATA_STR, MODDATATYPE_MEMBER);
    251 
    252 	if (!md || !md->unserialize)
    253 		return;
    254 
    255 	md->unserialize(MOD_DATA_INVISIBLE, &moddata_member(m, md));
    256 }
    257 
    258 
    259 int delayjoin_is_ok(Client *client, Channel *channel, char mode, const char *para, int checkt, int what)
    260 {
    261 	return EX_ALWAYS_DENY;
    262 }
    263 
    264 
    265 int visible_in_channel(Client *client, Channel *channel, Member *client_member)
    266 {
    267 	return (channel_is_delayed(channel) || channel_is_post_delayed(channel)) && moded_member_invisible(client_member, channel);
    268 }
    269 
    270 int moded_join(Client *client, Channel *channel)
    271 {
    272 	if (channel_is_delayed(channel))
    273 		set_user_invisible(channel, client);
    274 
    275 	return 0;
    276 }
    277 
    278 int moded_part(Client *client, Channel *channel, MessageTag *mtags, const char *comment)
    279 {
    280 	if (channel_is_delayed(channel) || channel_is_post_delayed(channel))
    281 		clear_user_invisible(channel, client);
    282 
    283 	return 0;
    284 }
    285 
    286 int moded_quit(Client *client, MessageTag *mtags, const char *comment)
    287 {
    288 	Membership *membership;
    289 	Channel *channel;
    290 
    291 	for (membership = client->user->channel; membership; membership=membership->next)
    292 	{
    293 		channel = membership->channel;
    294 		/* Identical to moded_part() */
    295 		if (channel_is_delayed(channel) || channel_is_post_delayed(channel))
    296 			clear_user_invisible(channel, client);
    297 	}
    298 
    299 	return 0;
    300 }
    301 
    302 int moded_chanmode(Client *client, Channel *channel, MessageTag *recv_mtags, const char *modebuf, const char *parabuf, time_t sendts, int samode)
    303 {
    304 	long CAP_EXTENDED_JOIN = ClientCapabilityBit("extended-join");
    305 
    306 	// Handle case where we just unset +D but have invisible users
    307 	if (!channel_is_delayed(channel) && !channel_is_post_delayed(channel) && channel_has_invisible_users(channel))
    308 		set_post_delayed(channel);
    309 	else if (channel_is_delayed(channel) && channel_is_post_delayed(channel))
    310 		clear_post_delayed(channel);
    311 
    312 	if ((channel_is_delayed(channel) || channel_is_post_delayed(channel)))
    313 	{
    314 		ParseMode pm;
    315 		int ret;
    316 		for (ret = parse_chanmode(&pm, modebuf, parabuf); ret; ret = parse_chanmode(&pm, NULL, NULL))
    317 		{
    318 			if (pm.what == MODE_ADD && (pm.modechar == 'o' || pm.modechar == 'h' || pm.modechar == 'a' || pm.modechar == 'q' || pm.modechar == 'v'))
    319 			{
    320 				Member* i;
    321 				Client *user = find_client(pm.param,NULL);
    322 				if (!user)
    323 					continue;
    324 
    325 				if (moded_user_invisible(user, channel))
    326 					clear_user_invisible_announce(channel, user, recv_mtags);
    327 
    328 				if (pm.modechar == 'v' || !MyConnect(user))
    329 					continue;
    330 
    331 				/* Our user 'user' just got ops (oaq) - send the joins for all the users (s)he doesn't know about */
    332 				for (i = channel->members; i; i = i->next)
    333 				{
    334 					if (i->client == user)
    335 						continue;
    336 					if (moded_user_invisible(i->client, channel))
    337 					{
    338 						MessageTag *mtags = NULL;
    339 						new_message_special(i->client, recv_mtags, &mtags, ":%s JOIN %s", i->client->name, channel->name);
    340 						if (HasCapabilityFast(user, CAP_EXTENDED_JOIN))
    341 						{
    342 							sendto_one(user, mtags, ":%s!%s@%s JOIN %s %s :%s",
    343 							           i->client->name, i->client->user->username, GetHost(i->client),
    344 							           channel->name,
    345 							           IsLoggedIn(i->client) ? i->client->user->account : "*",
    346 							           i->client->info);
    347 						} else {
    348 							sendto_one(user, mtags, ":%s!%s@%s JOIN :%s", i->client->name, i->client->user->username, GetHost(i->client), channel->name);
    349 						}
    350 						free_message_tags(mtags);
    351 					}
    352 				}
    353 
    354 			}
    355 			if (pm.what == MODE_DEL && (pm.modechar == 'o' || pm.modechar == 'h' || pm.modechar == 'a' || pm.modechar == 'q' || pm.modechar == 'v'))
    356 			{
    357 				Member* i;
    358 				Client *user = find_client(pm.param,NULL);
    359 				if (!user)
    360 					continue;
    361 
    362 				if (moded_user_invisible(user, channel))
    363 					clear_user_invisible_announce(channel, user, recv_mtags);
    364 
    365 				if (pm.modechar == 'v' || !MyConnect(user))
    366 					continue;
    367 
    368 				/* Our user 'user' just lost ops (oaq) - send the parts for all users (s)he won't see anymore */
    369 				for (i = channel->members; i; i = i->next)
    370 				{
    371 					if (i->client == user)
    372 						continue;
    373 					if (moded_user_invisible(i->client, channel))
    374 					{
    375 						MessageTag *mtags = NULL;
    376 						new_message_special(i->client, recv_mtags, &mtags, ":%s PART %s", i->client->name, channel->name);
    377 						sendto_one(user, mtags, ":%s!%s@%s PART :%s", i->client->name, i->client->user->username, GetHost(i->client), channel->name);
    378 						free_message_tags(mtags);
    379 					}
    380 				}
    381 
    382 			}
    383 		}
    384 	}
    385 
    386 	return 0;
    387 }
    388 
    389 int moded_prechanmsg(Client *client, Channel *channel, MessageTag **mtags, const char *text, SendType sendtype)
    390 {
    391 	if ((channel_is_delayed(channel) || channel_is_post_delayed(channel)) && (moded_user_invisible(client, channel)))
    392 		clear_user_invisible_announce(channel, client, *mtags);
    393 
    394 	return 0;
    395 }
    396 
    397 const char *moded_serialize(ModData *m)
    398 {
    399 	return m->i ? "1" : "0";
    400 }
    401 
    402 void moded_unserialize(const char *str, ModData *m)
    403 {
    404 	m->i = atoi(str);
    405 }