def __try_change_control__(self, session_id, moderator, info, flag): found = True try: control = group.Control(ord(flag)) if info.control == control: self.broker.deliver(session_id, ltd.encode_str("e", "Group is already %s." % str(control))) else: info.control = control if control == group.Control.PUBLIC: self.broker.to_channel(str(info), ltd.encode_status_msg("Change", "%s made group public." % moderator)) else: self.broker.to_channel(str(info), ltd.encode_status_msg("Change", "%s is now %s." % (str(info), str(control)))) info.clear_talkers() if control == group.Control.MODERATED: info.moderator = session_id self.broker.to_channel(info.key, ltd.encode_status_msg("Pass", "%s is now mod." % moderator)) if control == group.Control.RESTRICTED: self.__make_restricted__(session_id, info) else: info.clear_invitations() except ValueError: found = False return found
def cancel(self, session_id, invitee, mode="n", quiet=None): quiet = bool(quiet) info = self.__get_group_if_can_moderate__(session_id) if not info.control == group.Control.RESTRICTED: raise LtdErrorException("The group isn't restricted.") try: if mode == "n": info.cancel_nick(invitee) loggedin_session = self.session.find_nick(invitee) if loggedin_session: state = self.session.get(session_id) self.broker.deliver(loggedin_session, ltd.encode_status_msg("FYI", "Invitation to group %s cancelled by %s." % (state.group, state.nick))) else: info.cancel_address(invitee) except KeyError: raise LtdErrorException("%s isn't invited." % invitee) if not quiet: self.broker.deliver(session_id, ltd.encode_status_msg("FYI", "%s cancelled." % invitee)) self.groups.update(info)
def pass_over(self, session_id, nick): info = self.__get_group_if_can_moderate__(session_id) loggedin_session = self.session.find_nick(nick) if not loggedin_session: raise LtdErrorException("%s is not signed on." % nick) if info.moderator == loggedin_session: raise LtdErrorException("You are already moderator.") loggedin_state = self.session.get(loggedin_session) if loggedin_state.nick.lower() == core.NICKSERV.lower(): raise LtdErrorException("Cannot pass to %s." % core.NICKSERV) info.moderator = loggedin_session self.groups.update(info) state = self.session.get(session_id) self.broker.deliver(loggedin_session, ltd.encode_status_msg("Pass", "%s just passed moderation of group %s." % (state.nick, info.display_name))) if info.volume != group.Volume.QUIET: self.broker.to_channel(info.key, ltd.encode_status_msg("Pass", "%s has passed moderation to %s." % (state.nick, loggedin_state.nick)))
def __notify__(self, session_id, signed_on): state = self.session.get(session_id) info = None if state.group: info = self.groups.get(state.group) status_name = "Notify-%s" % ("On" if signed_on else "Off") for k, v in self.session: if k != session_id and v.nick != core.NICKSERV and not v.notifylist.empty( ): hidden = not info or info.visibility == group.Visibility.INVISIBLE same_group = v.group and v.group.lower() == info.key if not hidden or same_group: if v.notifylist.nick_watched( state.nick) and not same_group: self.broker.deliver( k, ltd.encode_status_msg(status_name, state.nick)) if v.notifylist.site_watched( state.address) and not same_group: self.broker.deliver( k, ltd.encode_status_msg(status_name, state.address))
def list(self, session_id): state = self.session.get(session_id) empty = True for nick in state.hushlist.nicks: empty = False if nick.private: self.broker.deliver(session_id, ltd.encode_status_msg("Personal-Nick-Hushed", nick.display_name)) if nick.public: self.broker.deliver(session_id, ltd.encode_status_msg("Open-Nick-Hushed", nick.display_name)) for site in state.hushlist.sites: empty = False if site.private: self.broker.deliver(session_id, ltd.encode_status_msg("Personal-Site-Hushed", site.display_name)) if site.public: self.broker.deliver(session_id, ltd.encode_status_msg("Open-Site-Hushed", site.display_name)) if empty: self.broker.deliver(session_id, ltd.encode_status_msg("Hush-List", "Empty List."))
def __login_no_password__(self, session_id, loginid, nick): self.log.debug("No password given, skipping authentication.") loggedin_session = self.session.find_nick(nick) if loggedin_session: self.log.debug("%s already logged in, aborting login.", nick) raise LtdStatusException("Register", "Nick already in use.") with self.nickdb_connection.enter_scope() as scope: if self.nickdb.exists(scope, nick): if self.nickdb.is_admin(scope, nick): raise LtdErrorException( "You need a password to login as administrator.") self.broker.deliver( session_id, ltd.encode_status_msg( "Register", "Send password to authenticate your nickname.")) else: self.broker.deliver( session_id, ltd.encode_status_msg( "No-Pass", "Your nickname does not have a password.")) self.broker.deliver( session_id, ltd.encode_status_msg("No-Pass", "For help type /m server ?")) self.session.update(session_id, loginid=loginid, nick=nick)
def __make_restricted__(self, session_id, info): for sub_id in self.broker.get_subscribers(str(info)): sub_state = self.session.get(sub_id) try: info.invite_nick(sub_state.nick, sub_state.authenticated) self.broker.deliver(session_id, ltd.encode_status_msg("FYI", "%s invited." % sub_state.nick)) self.broker.deliver(sub_id, ltd.encode_status_msg("FYI", "You are invited to group %s by default." % sub_state.group)) except OverflowError: self.broker.deliver(session_id, ltd.encode_str("e", "Invitation list is full."))
def notify_messagebox(self, session_id): with self.nickdb_connection.enter_scope() as scope: state = self.session.get(session_id) if self.nickdb.exists(scope, state.nick): count = self.nickdb.count_messages(scope, state.nick) limit = self.nickdb.get_mbox_limit(scope, state.nick) if count > 0: self.broker.deliver(session_id, ltd.encode_status_msg("Message", "You have %d message%s." % (count, "" if count == 1 else "s"))) if count >= limit: self.broker.deliver(session_id, ltd.encode_status_msg("Message", "User mailbox is full."))
def change_password(self, session_id, nick, password): state = self.session.get(session_id) if state.nick.lower() == nick.lower(): if not state.authenticated: raise LtdErrorException("You must be registered to change your password.") else: self.__test_admin__(session_id) self.log.debug("Changing password of user %s." % nick) with self.nickdb_connection.enter_scope() as scope: if not self.nickdb.exists(scope, nick): raise LtdErrorException("%s not found." % nick) self.log.debug("Nick found, changing password.") if not validate.is_valid_password(password): raise LtdStatusException("Password", "Password format not valid. Password length must be between %d and %d characters." % (validate.PASSWORD_MIN, validate.PASSWORD_MAX)) self.nickdb.set_password(scope, nick, password) self.broker.deliver(session_id, ltd.encode_status_msg("Pass", "Password changed.")) scope.complete()
def __deny_login__(self, session_id, argv): usage = "Usage: ipfilter deny {filter} {seconds}" if not argv or len(argv) > 2: raise LtdErrorException(usage) filter = None try: filter = ipfilter.Factory.create(argv[0]) except: raise LtdErrorException("Filter is malformed.") ttl = -1 try: if len(argv) == 2: ttl = int(argv[1]) if ttl < 0: raise ValueError except: raise LtdErrorException(usage) with self.__ipfilter_connection.enter_scope() as scope: self.__ipfilters.deny(scope, filter, ttl) lifetime = "forever" if ttl > -1: lifetime = dateutils.elapsed_time(ttl) self.broker.deliver(session_id, ltd.encode_status_msg("IP-Filter", "%s denied (%s)." % (argv[0], lifetime))) scope.complete()
def boot(self, session_id, nick): info = self.__get_group_if_can_moderate__(session_id) loggedin_session = self.session.find_nick(nick) if loggedin_session == session_id: raise LtdErrorException("You cannot boot yourself.") if not loggedin_session: raise LtdErrorException("%s is not signed on." % nick) state = self.session.get(session_id) loggedin_state = self.session.get(loggedin_session) if loggedin_state.group.lower() != state.group.lower(): raise LtdErrorException("%s is not in your group." % nick) if loggedin_state.authenticated: with self.nickdb_connection.enter_scope() as scope: if self.nickdb.is_admin(scope, state.nick): self.broker.deliver(loggedin_session, ltd.encode_status_msg("Boot", "%s tried to boot you." % state.nick)) self.reputation.fatal(session_id) raise LtdErrorException("You cannot boot an admin.") try: info.cancel_nick(loggedin_state.nick) self.broker.deliver(session_id, ltd.encode_status_msg("FYI", "%s cancelled." % nick)) except KeyError: pass try: info.mute_nick(loggedin_state.nick) self.broker.deliver(session_id, ltd.encode_status_msg("FYI", "%s removed from talker list." % nick)) except KeyError: pass self.broker.to_channel(info.key, ltd.encode_status_msg("Boot", "%s was booted." % nick)) self.broker.deliver(loggedin_session, ltd.encode_status_msg("Boot", "%s booted you." % state.nick)) ACTION(actions.usersession.UserSession).join(loggedin_session, core.BOOT_GROUP) with self.statsdb_connection.enter_scope() as scope: self.statsdb.add_boot(scope) scope.complete()
def drop_moderator(self, session_id, category, message): for info in self.groups.get_groups(): if info.moderator == session_id: if info.volume != group.Volume.QUIET: self.broker.to_channel_from( session_id, info.key, ltd.encode_status_msg(category, message)) new_mod = None min_elapsed = -1 for sub_id in self.broker.get_subscribers(info.key): sub_state = self.session.get(sub_id) elapsed = sub_state.t_recv.elapsed() self.log.debug( "Found subscriber: %s, elapsed milliseconds: %f", sub_state.nick, elapsed) if not sub_state.away and (min_elapsed == -1 or elapsed < min_elapsed): min_elapsed = elapsed new_mod = [sub_id, sub_state.nick] if new_mod: self.log.debug("New mod: %s", new_mod[1]) self.broker.to_channel( info.key, ltd.encode_status_msg("Pass", "%s is now mod." % new_mod[1])) info.moderator = new_mod[0] else: self.log.debug("No new moderator found.") self.broker.to_channel( str(info), ltd.encode_status_msg("Change", "Group is now public.")) info.control = group.Control.PUBLIC info.moderator = None info.clear_talkers() info.clear_invitations() self.groups.update(info)
def drop(self, session_id, nicks): state = self.session.get(session_id) with self.statsdb_connection.enter_scope() as scope: for nick in nicks: victim_id = self.session.find_nick(nick) if victim_id: self.broker.deliver(session_id, ltd.encode_status_msg("Drop", "You have dropped %s." % nick)) self.broker.deliver(victim_id, ltd.encode_status_msg("Drop", "You have been disconnected by %s." % state.nick)) self.broker.deliver(victim_id, ltd.encode_empty_cmd("g")) self.statsdb.add_drop(scope) else: self.broker.deliver(session_id, ltd.encode_str("e", "%s not found." % nick)) scope.complete()
def invite(self, session_id, invitee, mode="n", quiet=None, registered=None): quiet = bool(quiet) registered = bool(registered) info = self.__get_group_if_can_moderate__(session_id) if not info.control == group.Control.RESTRICTED: raise LtdErrorException("The group isn't restricted.") state = self.session.get(session_id) loggedin_session = None try: if mode == "n": if registered: with self.nickdb_connection.enter_scope() as scope: if not self.nickdb.exists(scope, invitee): raise LtdErrorException("User not found.") loggedin_session = self.session.find_nick(invitee) if not registered and not loggedin_session: raise LtdErrorException("%s is not signed on." % invitee) info.invite_nick(invitee, registered) if loggedin_session: loggedin_state = self.session.get(loggedin_session) if registered and not loggedin_state.authenticated: self.broker.deliver(loggedin_session, ltd.encode_status_msg("RSVP", "You need to be registered to enter group %s." % str(info))) else: self.broker.deliver(loggedin_session, ltd.encode_status_msg("RSVP", "You are invited to group %s by %s." % (str(info), state.nick))) else: info.invite_address(invitee, registered) except OverflowError: raise LtdErrorException("Invitation list is full.") if not quiet: self.broker.deliver(session_id, ltd.encode_status_msg("FYI", "%s invited%s." % (invitee, " (registered only)" if registered else ""))) self.groups.update(info)
def noaway(self, session_id): state = self.session.get(session_id) if not state.away: raise LtdStatusException("Away", "No away message set.") self.session.update(session_id, away=None, t_away=None) self.broker.deliver( session_id, ltd.encode_status_msg("Away", "Away message unset."))
def relinquish(self, session_id): info = self.__get_group_if_can_moderate__(session_id) info.moderator = None state = self.session.get(session_id) if info.volume != group.Volume.QUIET: self.broker.to_channel(info.key, ltd.encode_status_msg("Change", "%s just relinquished moderation." % state.nick)) if info.control != group.Control.PUBLIC: info.control = group.Control.PUBLIC info.clear_talkers() info.clear_invitations() if info.volume != group.Volume.QUIET: self.broker.to_channel(info.key, ltd.encode_status_msg("Change", "Group is now public.")) self.groups.update(info)
def __flush_ip_filters__(self, session_id, argv): if argv: raise LtdErrorException("Usage: ipfilter flush") with self.__ipfilter_connection.enter_scope() as scope: self.__ipfilters.flush(scope) scope.complete() self.broker.deliver(session_id, ltd.encode_status_msg("IP-Filter", "Flushed."))
def __change_idle_mod__(self, moderator, info, minutes): old_val = info.idle_mod info.idle_mod = minutes self.broker.to_channel(info.key, ltd.encode_status_msg("Change", "%s changed idle-mod to %s." % (moderator, info.idle_mod_str))) if old_val > minutes and minutes > 0 and info.moderator: mod_state = self.session.get(info.moderator) if mod_state.t_recv.elapsed() > (minutes * 60): ACTION(actions.usersession.UserSession).idle_mod(info.moderator)
def list(self, session_id): state = self.session.get(session_id) empty = True for nick in state.notifylist.nicks: empty = False self.broker.deliver(session_id, ltd.encode_status_msg("Notify-Nickname", nick)) for site in state.notifylist.sites: empty = False self.broker.deliver(session_id, ltd.encode_status_msg("Notify-Site", site)) if empty: self.broker.deliver( session_id, ltd.encode_status_msg("Notify-List", "Empty List."))
def __show_ip_filters__(self, session_id, argv): if argv: raise LtdErrorException("Usage: ipfilter show") filters = [] with self.__ipfilter_connection.enter_scope() as scope: for f, l in self.__ipfilters.load_deny_filters(scope): lifetime = "forever" if l > -1: time_left = max(1, l - dateutils.timestamp()) lifetime = dateutils.elapsed_time(time_left) filters.append((f.expression, lifetime)) if filters: for entry in sorted(filters, key=lambda e: "@".join(reversed(e[0].split("@", 1))).lower()): self.broker.deliver(session_id, ltd.encode_status_msg("IP-Filter", "%s denied (%s)" % entry)) else: self.broker.deliver(session_id, ltd.encode_status_msg("IP-Filter", "List is empty."))
def idle_boot(self, session_id): self.broker.deliver( session_id, ltd.encode_status_msg("Idle-Boot", "You were booted.")) self.join(session_id, core.BOOT_GROUP) self.drop_moderator(session_id, "Idle-Boot", "Your group moderator idled away.") with self.statsdb_connection.enter_scope() as scope: self.statsdb.add_idleboot(scope) scope.complete()
def beep(self, session_id, receiver): loggedin_session = self.session.find_nick(receiver) if not loggedin_session: raise LtdErrorException("%s is not signed on." % receiver) loggedin_state = self.session.get(loggedin_session) state = self.session.get(session_id) if state.echo == session.EchoMode.VERBOSE: self.broker.deliver( session_id, ltd.encode_co_output("<*to: %s*> [=Beep=]" % receiver)) if loggedin_state.beep != session.BeepMode.ON: if loggedin_state.beep == session.BeepMode.VERBOSE: self.broker.deliver( loggedin_session, ltd.encode_status_msg( "No-Beep", "%s attempted (and failed) to beep you." % state.nick)) raise LtdStatusException("Beep", "User has nobeep enabled.") self.broker.deliver(loggedin_session, ltd.encode_str("k", state.nick)) if loggedin_state.away: if not self.away_table.is_alive(session_id, receiver): self.broker.deliver( session_id, ltd.encode_status_msg( "Away", "%s (since %s)." % (loggedin_state.away, loggedin_state.t_away.elapsed_str()))) self.away_table.set_alive(session_id, receiver, self.config.timeouts_away_message)
def away(self, session_id, text): state = self.session.get(session_id) if text: if len(text) > 64: text = "%s..." % text[:61] self.session.update(session_id, away=text, t_away=Timer()) self.broker.deliver( session_id, ltd.encode_status_msg("Away", "Away message set to \"%s\"." % text)) elif state.away: self.broker.deliver( session_id, ltd.encode_status_msg( "Away", "Away message is set to \"%s\" (since %s)." % (state.away, state.t_away.elapsed_str()))) else: self.broker.deliver( session_id, ltd.encode_status_msg("Away", "Away message is not set."))
def set_topic(self, session_id, topic): info = self.__get_group_if_can_moderate__(session_id, allow_public=True) if not validate.is_valid_topic(topic): raise LtdErrorException("Topic must consist of at least %d and at most %d characters." % (validate.TOPIC_MIN, validate.TOPIC_MAX)) info.topic = topic self.groups.update(info) if info.volume != group.Volume.QUIET: self.broker.to_channel(str(info), ltd.encode_status_msg("Topic", "%s changed the topic to \"%s\"" % (self.session.get(session_id).nick, topic)))
def set_mode(self, session_id, mode): if not mode in ["on", "off", "verbose"]: raise LtdErrorException("Usage: /nobeep on|off|verbose") real_mode = session.BeepMode.ON if mode == "on": real_mode = session.BeepMode.OFF elif mode == "verbose": real_mode = session.BeepMode.VERBOSE self.session.update(session_id, beep=real_mode) self.broker.deliver( session_id, ltd.encode_status_msg("No-Beep", "No-Beep is now %s." % mode))
def set_mode(self, session_id, mode): if not mode in ["on", "off", "verbose"]: raise LtdErrorException("Usage: /echoback on|off|verbose") real_mode = session.EchoMode.OFF if mode == "on": real_mode = session.EchoMode.ON elif mode == "verbose": real_mode = session.EchoMode.VERBOSE self.session.update(session_id, echo=real_mode) self.broker.deliver( session_id, ltd.encode_status_msg("Echo", "Echoback is now %s." % mode))
def __try_change_visibility__(self, session_id, nick, info, flag): found = True try: visibility = group.Visibility(ord(flag)) if info.visibility == visibility: self.broker.deliver(session_id, ltd.encode_str("e", "Group is already %s." % str(visibility))) else: info.visibility = visibility self.broker.to_channel(str(info), ltd.encode_status_msg("Change", "%s made group %s." % (nick, str(visibility)))) except ValueError: found = False return found
def mark_registered(self, session_id): with self.nickdb_connection.enter_scope() as scope: state = self.session.get(session_id) self.nickdb.set_lastlogin(scope, state.nick, state.loginid, state.host) now = dateutils.now() self.nickdb.set_signon(scope, state.nick, now) self.session.update(session_id, signon=now, authenticated=True) self.broker.deliver(session_id, ltd.encode_status_msg("Register", "Nick registered.")) self.reputation.good(session_id) scope.complete()
def change_password(self, session_id, old_pwd, new_pwd): self.log.debug("Changing user password.") state = self.session.get(session_id) is_reset_code = False with self.password_reset_connection.enter_scope() as scope: is_reset_code = self.password_reset.has_pending_request(scope, state.nick, old_pwd, self.config.timeouts_password_reset_code) with self.nickdb_connection.enter_scope() as scope: if not self.nickdb.exists(scope, state.nick): raise LtdErrorException("Authorization failure.") if not is_reset_code: self.log.debug("Validating password.") if not state.authenticated: self.reputation.critical(session_id) raise LtdErrorException("You must be registered to change your password.") if not self.nickdb.check_password(scope, state.nick, old_pwd): self.reputation.fatal(session_id) raise LtdErrorException("Authorization failure.") else: self.log.debug("Password reset code found: %s", old_pwd) if not validate.is_valid_password(new_pwd): raise LtdStatusException("Pass", "Password format not valid. Password length must be between %d and %d characters." % (validate.PASSWORD_MIN, validate.PASSWORD_MAX)) self.nickdb.set_password(scope, state.nick, new_pwd) self.broker.deliver(session_id, ltd.encode_status_msg("Pass", "Password changed.")) scope.complete() if is_reset_code: with self.password_reset_connection.enter_scope() as scope: self.password_reset.delete_requests(scope, state.nick) scope.complete()
def __change_idle_boot__(self, moderator, info, minutes): old_val = info.idle_boot info.idle_boot = minutes self.broker.to_channel(info.key, ltd.encode_status_msg("Change", "%s changed idle-boot to %s." % (moderator, info.idle_boot_str))) if old_val > minutes and minutes > 0: boot_ids = [] for sub_id in self.broker.get_subscribers(str(info)): sub_state = self.session.get(sub_id) if (not info.moderator or sub_id != info.moderator): if sub_state.t_recv.elapsed() > (info.idle_boot * 60): boot_ids.append(sub_id) for sub_id in boot_ids: ACTION(actions.usersession.UserSession).idle_boot(sub_id)