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 }