unrealircd

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

delayjoin.c (11310B)

      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);
     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)
    266 {
    267 	return (channel_is_delayed(channel) || channel_is_post_delayed(channel)) && moded_user_invisible(client, channel);
    268 }
    269 
    270 
    271 int moded_join(Client *client, Channel *channel)
    272 {
    273 	if (channel_is_delayed(channel))
    274 		set_user_invisible(channel, client);
    275 
    276 	return 0;
    277 }
    278 
    279 int moded_part(Client *client, Channel *channel, MessageTag *mtags, const char *comment)
    280 {
    281 	if (channel_is_delayed(channel) || channel_is_post_delayed(channel))
    282 		clear_user_invisible(channel, client);
    283 
    284 	return 0;
    285 }
    286 
    287 int moded_quit(Client *client, MessageTag *mtags, const char *comment)
    288 {
    289 	Membership *membership;
    290 	Channel *channel;
    291 
    292 	for (membership = client->user->channel; membership; membership=membership->next)
    293 	{
    294 		channel = membership->channel;
    295 		/* Identical to moded_part() */
    296 		if (channel_is_delayed(channel) || channel_is_post_delayed(channel))
    297 			clear_user_invisible(channel, client);
    298 	}
    299 
    300 	return 0;
    301 }
    302 
    303 int moded_chanmode(Client *client, Channel *channel, MessageTag *recv_mtags, const char *modebuf, const char *parabuf, time_t sendts, int samode)
    304 {
    305 	long CAP_EXTENDED_JOIN = ClientCapabilityBit("extended-join");
    306 
    307 	// Handle case where we just unset +D but have invisible users
    308 	if (!channel_is_delayed(channel) && !channel_is_post_delayed(channel) && channel_has_invisible_users(channel))
    309 		set_post_delayed(channel);
    310 	else if (channel_is_delayed(channel) && channel_is_post_delayed(channel))
    311 		clear_post_delayed(channel);
    312 
    313 	if ((channel_is_delayed(channel) || channel_is_post_delayed(channel)))
    314 	{
    315 		ParseMode pm;
    316 		int ret;
    317 		for (ret = parse_chanmode(&pm, modebuf, parabuf); ret; ret = parse_chanmode(&pm, NULL, NULL))
    318 		{
    319 			if (pm.what == MODE_ADD && (pm.modechar == 'o' || pm.modechar == 'h' || pm.modechar == 'a' || pm.modechar == 'q' || pm.modechar == 'v'))
    320 			{
    321 				Member* i;
    322 				Client *user = find_client(pm.param,NULL);
    323 				if (!user)
    324 					continue;
    325 
    326 				if (moded_user_invisible(user, channel))
    327 					clear_user_invisible_announce(channel, user, recv_mtags);
    328 
    329 				if (pm.modechar == 'v' || !MyConnect(user))
    330 					continue;
    331 
    332 				/* Our user 'user' just got ops (oaq) - send the joins for all the users (s)he doesn't know about */
    333 				for (i = channel->members; i; i = i->next)
    334 				{
    335 					if (i->client == user)
    336 						continue;
    337 					if (moded_user_invisible(i->client, channel))
    338 					{
    339 						MessageTag *mtags = NULL;
    340 						new_message_special(i->client, recv_mtags, &mtags, ":%s JOIN %s", i->client->name, channel->name);
    341 						if (HasCapabilityFast(user, CAP_EXTENDED_JOIN))
    342 						{
    343 							sendto_one(user, mtags, ":%s!%s@%s JOIN %s %s :%s",
    344 							           i->client->name, i->client->user->username, GetHost(i->client),
    345 							           channel->name,
    346 							           IsLoggedIn(i->client) ? i->client->user->account : "*",
    347 							           i->client->info);
    348 						} else {
    349 							sendto_one(user, mtags, ":%s!%s@%s JOIN :%s", i->client->name, i->client->user->username, GetHost(i->client), channel->name);
    350 						}
    351 						free_message_tags(mtags);
    352 					}
    353 				}
    354 
    355 			}
    356 			if (pm.what == MODE_DEL && (pm.modechar == 'o' || pm.modechar == 'h' || pm.modechar == 'a' || pm.modechar == 'q' || pm.modechar == 'v'))
    357 			{
    358 				Member* i;
    359 				Client *user = find_client(pm.param,NULL);
    360 				if (!user)
    361 					continue;
    362 
    363 				if (moded_user_invisible(user, channel))
    364 					clear_user_invisible_announce(channel, user, recv_mtags);
    365 
    366 				if (pm.modechar == 'v' || !MyConnect(user))
    367 					continue;
    368 
    369 				/* Our user 'user' just lost ops (oaq) - send the parts for all users (s)he won't see anymore */
    370 				for (i = channel->members; i; i = i->next)
    371 				{
    372 					if (i->client == user)
    373 						continue;
    374 					if (moded_user_invisible(i->client, channel))
    375 					{
    376 						MessageTag *mtags = NULL;
    377 						new_message_special(i->client, recv_mtags, &mtags, ":%s PART %s", i->client->name, channel->name);
    378 						sendto_one(user, mtags, ":%s!%s@%s PART :%s", i->client->name, i->client->user->username, GetHost(i->client), channel->name);
    379 						free_message_tags(mtags);
    380 					}
    381 				}
    382 
    383 			}
    384 		}
    385 	}
    386 
    387 	return 0;
    388 }
    389 
    390 int moded_prechanmsg(Client *client, Channel *channel, MessageTag *mtags, const char *text, SendType sendtype)
    391 {
    392 	if ((channel_is_delayed(channel) || channel_is_post_delayed(channel)) && (moded_user_invisible(client, channel)))
    393 		clear_user_invisible_announce(channel, client, mtags);
    394 
    395 	return 0;
    396 }
    397 
    398 const char *moded_serialize(ModData *m)
    399 {
    400 	return m->i ? "1" : "0";
    401 }
    402 
    403 void moded_unserialize(const char *str, ModData *m)
    404 {
    405 	m->i = atoi(str);
    406 }