def join(self, ircd, channel): mod = next((m for m in ircd.channel_mode_class if m.mode == chmode), None) if not mod: logging.error(f"Module for channele mode '{chmode}' not found.") return if not hasattr(channel, mod.list_name): setattr(channel, mod.list_name, {}) last_level = 0 total_modes, total_params = '+', '' for entry in channel.whitelist: level = int(entry.split(':')[0]) if level > last_level: # Found higher level. last_level = level mask = entry.split(':')[1] modes = '' if match(mask, self.fullmask()): if level >= 9999: modes += 'oq' elif level >= 10: modes += 'oa' elif level >= 5: modes += 'o' elif level >= 4: modes += 'h' elif level >= 1: modes += 'v' if modes: nicks = '{} '.format(self.nickname) * len(modes) total_modes += modes total_params += nicks if total_modes and total_params: ircd.handle('MODE', '{} {} {}'.format(channel.name, total_modes, total_params))
def join(self, ircd, channel): if chmode in channel.modes and 'j' in channel.chmodef and not self.ocheck( 'o', 'override') and self.server.eos: r = int(round(time.time() * 1000)) channel.joinQueue[r] = {} channel.joinQueue[r]['ctime'] = int(time.time()) if len(channel.joinQueue) > channel.chmodef['j']['amount']: channel.joinQueue = {} if channel.chmodef['j']['action'] == 'i': ircd.handle('MODE', '{} +i'.format(channel.name)) elif channel.chmodef['j']['action'] == 'R': ircd.handle('MODE', '{} +R'.format(channel.name)) channel.chmodef['j']['actionSet'] = int(time.time())
def msg(self, ircd, channel, msg): if chmode in channel.modes and 'm' in channel.chmodef and self.chlevel( channel) < 2 and 'o' not in self.modes: if self not in channel.messageQueue: channel.messageQueue[self] = {} channel.messageQueue[self]['ctime'] = time.time() channel.messageQueue[self][int(round(time.time() * 1000))] = None if len(channel.messageQueue[self]) > channel.chmodef['m']['amount']: p = {'sync': False} if channel.chmodef['m']['action'] == 'kick': ircd.handle( 'KICK', '{} {} :Flood! Limit is {} messages in {} seconds.'.format( channel.name, self.uid, channel.chmodef['m']['amount'], channel.chmodef['m']['time']), params=p) elif channel.chmodef['m']['action'] == 'b': duration = channel.chmodef['m']['duration'] ircd.handle( 'MODE', '{} +b ~t:{}:*@{}'.format(channel.name, duration, self.cloakhost)) ircd.handle( 'KICK', '{} {} :Flood! Limit is {} messages in {} seconds.'.format( channel.name, self.uid, channel.chmodef['m']['amount'], channel.chmodef['m']['time']), params=p) elif channel.chmodef['m']['action'] == 'm': ircd.handle('MODE', '{} +m'.format(channel.name)) channel.chmodef['m']['actionSet'] = int(time.time()) del channel.messageQueue[self]
def checkExpiredBans(ircd): remove_bans = {} for chan in ircd.channels: remove_bans[chan] = [] for ban in [ban for ban in chan.bans if ban and ban[:2] == '~t']: minutes = int(ban.split(':')[1]) * 60 banset = int(chan.bans[ban]['ctime']) if int(time.time()) >= (minutes + banset): remove_bans[chan].append(ban) for chan in remove_bans: if len(remove_bans[chan]) < 1: continue bans = ' '.join(remove_bans[chan]) tmodes = 'b' * len(remove_bans[chan]) ircd.handle('MODE', '{} -{} {} 0'.format(chan.name, tmodes, bans))
def checkExpiredFloods(ircd): # Checking for timed-out flood protection. channels = (channel for channel in ircd.channels if chmode in channel.modes and 'm' in channel.chmodef) for chan in channels: if chan.chmodef['m']['action'] == 'm' and 'm' in chan.modes: if chan.chmodef['m']['actionSet'] and int( time.time()) - chan.chmodef['m'][ 'actionSet'] > chan.chmodef['m']['duration'] * 60: ircd.handle('MODE', '{} -m'.format(chan.name)) chan.chmodef['m']['actionSet'] = None for user in (user for user in chan.users if user in dict(chan.messageQueue)): if time.time( ) - chan.messageQueue[user]['ctime'] > chan.chmodef['m']['time']: # print('Resetting flood for {} on {}'.format(user,chan)) del chan.messageQueue[user] channels = (channel for channel in ircd.channels if chmode in channel.modes and 'j' in channel.chmodef) for chan in channels: for entry in (entry for entry in dict(chan.joinQueue)): if int(time.time( )) - chan.joinQueue[entry]['ctime'] > chan.chmodef['j']['time']: # print('Resetting flood for {} on {}'.format(user, chan)) del chan.joinQueue[entry] if chan.chmodef['j']['action'] == 'i' and 'i' in chan.modes: if chan.chmodef['j']['actionSet'] and int( time.time()) - chan.chmodef['j'][ 'actionSet'] > chan.chmodef['j']['duration'] * 60: ircd.handle('MODE', '{} -i'.format(chan.name)) chan.chmodef['j']['actionSet'] = None elif chan.chmodef['j']['action'] == 'R' and 'R' in chan.modes: if chan.chmodef['j']['actionSet'] and int( time.time()) - chan.chmodef['j'][ 'actionSet'] > chan.chmodef['j']['duration'] * 60: ircd.handle('MODE', '{} -R'.format(chan.name)) chan.chmodef['j']['actionSet'] = None
def processModes(self, ircd, channel, recv, sync=True, sourceServer=None, sourceUser=None): logging.debug('processModes(): {} :: {}'.format(self, recv)) try: if sourceServer != ircd or (type(sourceUser).__name__ == 'User' and sourceUser.server != ircd): hook = 'remote_chanmode' else: hook = 'local_chanmode' rawModes = ' '.join(recv[1:]) if rawModes.startswith(':'): rawModes = rawModes[1:] if type(sourceUser).__name__ == 'User': displaySource = sourceUser.uid else: displaySource = sourceUser.sid # if not sourceServer.eos and sourceServer != ircd: # sync = False except Exception as ex: logging.exception(ex) try: # global modebuf, parambuf, action, prevaction, commandQueue modebuf, parambuf, commandQueue = [], [], [] action = '' prevaction = '' paramcount = -1 chmodes = ircd.chstatus ircd.parammodes = ircd.chstatus for x in range(0, 4): for m in [m for m in ircd.channel_modes[x] if m not in chmodes]: chmodes += m if str(x) in '012' and m not in ircd.parammodes: ircd.parammodes += m global oper_override extban_prefix = None if 'EXTBAN' in ircd.support: extban_prefix = ircd.support['EXTBAN'][0] # logging.info('Extban prefix set: {}'.format(extban_prefix)) # Setting some mode level shit. # +v = 1 # +h = 2 # +o = 3 # +a = 4 # +q = 5 # oper = 6 # server = 7 modeLevel = { # Channel statuses. Users with +h or higher can always remove their own status. # These numbers may seem off, but it's correct. This determines the level requirement to set levels. # For example, you can only give voice if you have 2 (+h) or higher, and only give ops with level 3 (+o) or higher. 'v': 2, 'h': 3, 'o': 3, 'a': 4, 'q': 5 } for t in ircd.channel_modes: for m in ircd.channel_modes[t]: level = ircd.channel_modes[t][m][0] modeLevel[m] = level # for m in [m for m in recv[1] if m in chmodes + '+-' or m in channel.modes]: warn = [] modes = recv[1][1:] if recv[1].startswith(':') else recv[1] for m in modes: if m not in chmodes + '+-' and m not in channel.modes and type( self).__name__ != 'Server': if m not in warn: self.sendraw(ircd.ERR.UNKNOWNMODE, f"{m} :unknown mode bar") warn.append(m) continue param_mode = None if m in ircd.parammodes: if (action == '+') or (action == '-' and m not in list( ircd.channel_modes[2]) + list(ircd.channel_modes[3])): # if (action == '+') or (action == '-' and m in list(ircd.channel_modes[0])+list(ircd.channel_modes[1])): paramcount += 1 if len(recv[2:]) > paramcount: param_mode = recv[2:][paramcount] logging.info('Param for {}{}: {}'.format( action, m, param_mode)) elif m not in ircd.channel_modes[0]: logging.warning( 'Received param mode {}{} without param'.format( action, m)) continue if m in '+-' and action != m: action = m # logging.debug('Action set: {}'.format(action)) if action != prevaction: if modebuf and modebuf[-1] in '-+': modebuf = modebuf[1:] modebuf.append(action) # logging.debug('Modebuf now: {}'.format(modebuf)) prevaction = action continue if not action: action = '+' if m in modeLevel and modeLevel[m] == 6 and ( type(self).__name__ != 'Server' and 'o' not in self.modes): continue if m in modeLevel and modeLevel[m] == 7 and type( self).__name__ != 'Server': continue if m not in '+-' and action != prevaction and ( (m in chmodes or m in ircd.chstatus) or (action in '+-' and m in channel.modes)): modebuf.append(action) prevaction = action if m not in ircd.chstatus and m not in '+-': if m in modeLevel and self.chlevel( channel) < modeLevel[m] and not self.ocheck( 'o', 'override'): continue elif m in modeLevel and self.chlevel( channel) < modeLevel[m] != 6: oper_override = True if m not in ircd.core_chmodes: c = next((x for x in ircd.channel_mode_class if x.mode == m), None) if c: if not c.check(channel, action, param_mode): continue c.modebuf = modebuf c.parambuf = parambuf if (action == '+' and c.set_mode(self, channel, param_mode) ) or (action == '-' and c.remove_mode(self, channel, param_mode)): pass else: # Modules like extbans do not have a mode, so we will check for hooks manually. for callable in [ callable for callable in ircd.hooks if callable[0].lower() == 'pre_' + hook and m in callable[1] ]: try: callable[2](self, ircd, channel, modebuf, parambuf, action, m, param_mode) except Exception as ex: logging.exception(ex) if action == '+' and (m in chmodes or type(self).__name__ == 'Server'): # SETTING CHANNEL MODES if m == 'l' and len(recv) > 2: if not param_mode.isdigit(): continue if int(param_mode) <= 0: continue if m in ircd.chan_params[channel] and int( ircd.chan_params[channel][m]) == int(param_mode): continue else: if m not in channel.modes: channel.modes += m modebuf.append(m) parambuf.append(param_mode) elif m == 'k' and m not in ircd.chan_params[channel]: if m not in channel.modes: channel.modes += m modebuf.append(m) parambuf.append(param_mode) elif m == 'L': param_mode = param_mode.split(',')[0] if param_mode[0] not in ircd.chantypes: continue redirect = None if 'L' not in channel.modes else ircd.chan_params[ channel]['L'] if redirect == param_mode or param_mode.lower( ) == channel.name.lower(): continue chan_exists = [ chan for chan in ircd.channels if chan.name.lower() == param_mode.lower() ] if not chan_exists: self.sendraw( 690, ':Target channel {} does not exist.'.format( param_mode)) continue if self.chlevel(chan_exists[0]) < 3 and not self.ocheck( 'o', 'override'): self.sendraw( 690, ':You must be opped on target channel {} to set it as redirect.' .format(chan_exists[0].name)) continue if 'L' in chan_exists[0].modes: self.sendraw(690, ':Destination channel already has +L.') continue elif self.chlevel(channel) < modeLevel[m]: oper_override = True if m not in channel.modes: channel.modes += m modebuf.append(m) parambuf.append(param_mode) elif m in 'beI': if extban_prefix and param_mode.startswith(extban_prefix): continue mask = make_mask(ircd, param_mode) if m == 'b': data = channel.bans s = 'ban' elif m == 'e': data = channel.excepts s = 'excepts' elif m == 'I': data = channel.invex s = 'invex' if mask not in data: if len(data) >= ircd.maxlist[m] and type( self).__name__ == 'User': self.sendraw( 478, '{} {} :Channel {} list is full'.format( channel.name, mask, s)) continue try: setter = self.fullmask() except: setter = self.hostname modebuf.append(m) parambuf.append(mask) data[mask] = {} data[mask]['setter'] = setter data[mask]['ctime'] = int(time.time()) continue elif m in ircd.chstatus: timed = False # + status temp_user = param_mode try: t = param_mode.split(':') temp_user = t[0] try: channel.temp_status except: channel.temp_status = {} if valid_expire(t[1]): timed = valid_expire(t[1]) except: pass user = list( filter( lambda u: u.uid == temp_user or u.nickname.lower() == temp_user.lower(), channel.users)) if not user: continue else: user = user[0] if m in channel.usermodes[user]: continue if type(self).__name__ != 'Server': if self.chlevel( channel) < modeLevel[m] and not self.ocheck( 'o', 'override'): continue elif self.chlevel(channel) < modeLevel[m]: oper_override = True channel.usermodes[user] += m modebuf.append(m) parambuf.append(user.nickname) if timed: channel.temp_status[user] = {} channel.temp_status[user][m] = {} channel.temp_status[user][m]['ctime'] = int( time.time()) + timed channel.temp_status[user][m]['action'] = '-' if m not in channel.modes and (m in list(ircd.channel_modes[3]) + list(ircd.channel_modes[2])): # If the mode is not handled by modules, do it here. if not next( (x for x in ircd.channel_mode_class if x.mode == m), None): modebuf.append(m) channel.modes += m logging.debug( 'Non-modulair mode "{}" has been handled by m_mode' .format(m)) if m == 'O' and len(channel.users) > 2: for user in [ user for user in channel.users if 'o' not in user.modes ]: cmd = ('KICK', '{} {} :Opers only'.format( channel.name, user.nickname)) commandQueue.append(cmd) elif action == '-' and ((m in chmodes or m in channel.modes) or type(self).__name__ == 'Server'): # REMOVING CHANNEL MODES if m in channel.modes: if m == 'l': # channel.limit = 0 if 'L' in channel.modes: # Also unset -L because we do not need it anymore. channel.modes = channel.modes.replace('L', '') modebuf.append('L') parambuf.append(ircd.chan_params[channel]['L']) elif m == 'k': if param_mode != ircd.chan_params[channel]['k']: continue parambuf.append(ircd.chan_params[channel][m]) elif m == 'L': parambuf.append(ircd.chan_params[channel]['L']) # channel.redirect = None elif m == 'P': if len(channel.users) == 0: ircd.channels.remove(channel) try: with open(ircd.rootdir + '/db/chans.db') as f: current_perm = f.read().split('\n')[0] current_perm = json.loads(current_perm) del current_perm[channel.name] with open(ircd.rootdir + '/db/chans.db', 'w+') as f: json.dump(current_perm, f) except Exception as ex: logging.debug(ex) if m in channel.modes: # Only remove mode if it's a core mode. ChannelMode class handles the rest. if m in ircd.core_chmodes: channel.modes = channel.modes.replace(m, '') modebuf.append(m) elif m in 'beI': mask = make_mask(ircd, param_mode) if m == 'b': data = channel.bans elif m == 'e': data = channel.excepts elif m == 'I': data = channel.invex if mask in data: del data[mask] parambuf.append(mask) modebuf.append(m) elif param_mode in data: del data[param_mode] parambuf.append(param_mode) modebuf.append(m) # continue elif m in ircd.chstatus: timed = False # -qaohv temp_user = param_mode try: t = param_mode.split(':') temp_user = t[0] try: channel.temp_status except: channel.temp_status = {} if valid_expire(t[1]): timed = valid_expire(t[1]) except: pass user = list( filter( lambda u: temp_user and u.uid == temp_user or u. nickname.lower() == temp_user.lower(), channel.users)) if not user: continue else: user = user[0] if m not in channel.usermodes[user]: continue if 'S' in user.modes and user.server.hostname in ircd.conf[ 'settings']['ulines'] and not self.ocheck( 'o', 'override'): self.sendraw( 974, '{} :{} is a protected service bot'.format( m, user.nickname)) continue elif 'S' in user.modes: oper_override = True if type(self).__name__ != 'Server': if self.chlevel( channel) < modeLevel[m] and not self.ocheck( 'o', 'override') and user != self: continue elif self.chlevel(channel) < modeLevel[m]: oper_override = True channel.usermodes[user] = channel.usermodes[user].replace( m, '') modebuf.append(m) parambuf.append(user.nickname) if timed: channel.temp_status[user] = {} channel.temp_status[user][m] = {} channel.temp_status[user][m]['ctime'] = int( time.time()) + timed channel.temp_status[user][m]['action'] = '+' if m in ircd.core_chmodes: # Finally, call modules for core modes. for callable in [ callable for callable in ircd.hooks if callable[0].lower() == 'pre_' + hook and m in callable[1] ]: try: callable[2](self, ircd, channel, modebuf, parambuf, action, m, param_mode) except Exception as ex: logging.exception(ex) continue if not re.sub('[+-]', '', ''.join(modebuf)): return while modebuf[-1] in '+-': modebuf = modebuf[:-1] if channel.name[0] == '&': sync = False modes = ''.join(modebuf) # total_modes, total_params = [], [] if len(modebuf) > 1: total_modes, total_params = [], [] paramcount = 0 action = '' for m in modes: if m in '+-': action = m total_modes.append(m) continue total_modes.append(m) if m in list(ircd.channel_modes[1]) + list( ircd.channel_modes[2]): # If a module handles a channel mode with a param, but for some reason forgets to add it to the chan_params dict, # we will add it here. It is really important that param-modes have their params saved. if action == '+': if m in ircd.core_chmodes: logging.debug( '[core] Storing param of {}: {}'.format( m, parambuf[paramcount])) ircd.chan_params[channel][m] = parambuf[paramcount] elif m not in ircd.chan_params[channel]: logging.debug( '[fallback] Storing param of {}: {}'.format( m, parambuf[paramcount])) ircd.chan_params[channel][m] = parambuf[paramcount] elif action == '-' and m in ircd.chan_params[channel]: logging.debug( '[fallback] Forgetting param of {}: {}'.format( m, ircd.chan_params[channel][m])) del ircd.chan_params[channel][m] for callable in [ callable for callable in ircd.hooks if callable[0].lower() == hook ]: try: callable[2](self, ircd, channel, modes, parambuf) except Exception as ex: logging.exception(ex) if m in ircd.parammodes and ( m not in ircd.channel_modes[2] or action == '+') and len(parambuf) > paramcount: # logging.debug(f"Paramcount: {paramcount}") # logging.debug(f"Parambuf: {action}{m} {parambuf}") total_params.append(parambuf[paramcount]) paramcount += 1 # logging.debug(f"Increased paramcount (now={paramcount}) - moving on") totalLength = len(''.join(total_modes) + ' ' + ' '.join(total_params)) mode_amount = len(re.sub('[+-]', '', ''.join(total_modes))) if mode_amount >= MAXMODES or totalLength >= 400: all_modes = ''.join(total_modes) + ' ' + ' '.join( total_params) if oper_override and type(self).__name__ != 'Server': sourceServer.snotice( 's', '*** OperOverride by {} ({}@{}) with MODE {} {}'. format(sourceUser.nickname, sourceUser.ident, sourceUser.hostname, channel.name, all_modes)) if sync: ircd.new_sync( ircd, sourceServer, ':{} MODE {} :{}'.format( displaySource, channel.name, all_modes if type(self).__name__ == 'User' else rawModes)) sourceUser.broadcast(channel.users, 'MODE {} {}'.format( channel.name, all_modes), source=sourceUser) total_modes, total_params = [action], [] continue if len(total_modes) > 1: all_modes = ''.join(total_modes) + ' ' + ' '.join(total_params) if oper_override and type(self).__name__ != 'Server': sourceServer.snotice( 's', '*** OperOverride by {} ({}@{}) with MODE {} {}'. format(sourceUser.nickname, sourceUser.ident, sourceUser.hostname, channel.name, all_modes)) if sync: ircd.new_sync( ircd, sourceServer, ':{} MODE {} :{}'.format( displaySource, channel.name, all_modes if type(self).__name__ == 'User' else rawModes)) sourceUser.broadcast(channel.users, 'MODE {} {}'.format( channel.name, all_modes), source=sourceUser) for cmd, data in commandQueue: ircd.handle(cmd, data) save_db(ircd) except Exception as ex: logging.exception(ex)