diff --git a/core/base.py b/core/base.py index d58fd7e..b7a8922 100644 --- a/core/base.py +++ b/core/base.py @@ -758,7 +758,7 @@ class Base: except TypeError: return None - def is_valid_ip(self, ip_to_control:str) -> bool: + def is_valid_ip(self, ip_to_control: str) -> bool: try: if ip_to_control in self.Config.WHITELISTED_IP: @@ -769,6 +769,26 @@ class Base: except ValueError: return False + def is_valid_email(self, email_to_control: str) -> bool: + """Check if the email is valid + + Args: + email_to_control (str): email to control + + Returns: + bool: True is the email is correct + """ + try: + pattern = '^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$' + if re.match(pattern, email_to_control): + return True + else: + return False + + except Exception as err: + self.logs.error(f'General Error: {err}') + return False + def decode_ip(self, ip_b64encoded: str) -> Union[str, None]: binary_ip = b64decode(ip_b64encoded) diff --git a/core/classes/client.py b/core/classes/client.py index aa7b029..fd54ff0 100644 --- a/core/classes/client.py +++ b/core/classes/client.py @@ -37,7 +37,7 @@ class Client: return True - def update(self, uid: str, newNickname: str) -> bool: + def update_nickname(self, uid: str, newNickname: str) -> bool: """Update the nickname starting from the UID Args: diff --git a/core/classes/protocols/inspircd.py b/core/classes/protocols/inspircd.py index eaea97f..39da912 100644 --- a/core/classes/protocols/inspircd.py +++ b/core/classes/protocols/inspircd.py @@ -401,7 +401,9 @@ class Inspircd: uid = str(serverMsg[1]).lstrip(':') newnickname = serverMsg[3] - self.__Irc.User.update(uid, newnickname) + self.__Irc.User.update_nickname(uid, newnickname) + self.__Irc.Client.update_nickname(uid, newnickname) + self.__Irc.Admin.update_nickname(uid, newnickname) return None diff --git a/core/classes/protocols/unreal6.py b/core/classes/protocols/unreal6.py index 8877343..8cc16bf 100644 --- a/core/classes/protocols/unreal6.py +++ b/core/classes/protocols/unreal6.py @@ -198,7 +198,7 @@ class Unrealircd6: self.send2socket(f":{self.__Config.SERVICE_NICKNAME} NICK {newnickname}") userObj = self.__Irc.User.get_User(self.__Config.SERVICE_NICKNAME) - self.__Irc.User.update(userObj.uid, newnickname) + self.__Irc.User.update_nickname(userObj.uid, newnickname) return None def squit(self, server_id: str, server_link: str, reason: str) -> None: @@ -459,6 +459,16 @@ class Unrealircd6: self.__Irc.Channel.delete_user_from_channel(channel, userObj.uid) return None + def send_mode_chan(self, channel_name: str, channel_mode: str) -> None: + + channel = self.__Irc.Channel.Is_Channel(channelToCheck=channel_name) + if not channel: + self.__Base.logs.error(f'The channel [{channel_name}] is not correct') + return None + + self.send2socket(f":{self.__Config.SERVICE_NICKNAME} MODE {channel_name} {channel_mode}") + return None + def send_raw(self, raw_command: str) -> None: self.send2socket(f":{self.__Config.SERVICE_NICKNAME} {raw_command}") @@ -631,8 +641,9 @@ class Unrealircd6: uid = str(serverMsg[1]).lstrip(':') newnickname = serverMsg[3] - self.__Irc.User.update(uid, newnickname) - self.__Irc.Client.update(uid, newnickname) + self.__Irc.User.update_nickname(uid, newnickname) + self.__Irc.Client.update_nickname(uid, newnickname) + self.__Irc.Admin.update_nickname(uid, newnickname) return None diff --git a/core/classes/user.py b/core/classes/user.py index ba7ce10..17451de 100644 --- a/core/classes/user.py +++ b/core/classes/user.py @@ -37,7 +37,7 @@ class User: return True - def update(self, uid: str, newNickname: str) -> bool: + def update_nickname(self, uid: str, newNickname: str) -> bool: """Update the nickname starting from the UID Args: diff --git a/core/irc.py b/core/irc.py index 0f71b39..f2ef8ff 100644 --- a/core/irc.py +++ b/core/irc.py @@ -98,6 +98,7 @@ class Irc: self.build_command(0, 'core', 'firstauth', 'First authentication of the Service') self.build_command(0, 'core', 'register', f'Register your nickname /msg {self.Config.SERVICE_NICKNAME} REGISTER ') self.build_command(0, 'core', 'identify', f'Identify yourself with your password /msg {self.Config.SERVICE_NICKNAME} IDENTIFY ') + self.build_command(0, 'core', 'logout', 'Reverse the effect of the identify command') self.build_command(1, 'core', 'load', 'Load an existing module') self.build_command(1, 'core', 'unload', 'Unload a module') self.build_command(1, 'core', 'reload', 'Reload a module') @@ -136,6 +137,7 @@ class Irc: ircInstance (Irc): Instance of Irc object. """ try: + self.init_service_user() self.__create_socket() self.__connect_to_irc(ircInstance) except AssertionError as ae: @@ -1233,79 +1235,151 @@ class Irc: case 'register': # Register PASSWORD EMAIL - password = cmd[1] - email = cmd[2] - user_obj = self.User.get_User(fromuser) + try: + + if len(cmd) < 3: + self.Protocol.send_notice( + nick_from=dnickname, + nick_to=fromuser, + msg=f'/msg {self.Config.SERVICE_NICKNAME} {command.upper()} ' + ) + return None + + password = cmd[1] + email = cmd[2] + + if not self.Base.is_valid_email(email_to_control=email): + self.Protocol.send_notice( + nick_from=dnickname, + nick_to=fromuser, + msg='The email is not valid. You must provide a valid email address (first.name@email.extension)' + ) + return None + + user_obj = self.User.get_User(fromuser) + + if user_obj is None: + self.Logs.error(f"Nickname ({fromuser}) doesn't exist, it is impossible to register this nickname") + return None + + # If the account already exist. + if self.Client.db_is_account_exist(fromuser): + self.Protocol.send_notice( + nick_from=dnickname, + nick_to=fromuser, + msg=f"Your account already exist, please try to login instead /msg {self.Config.SERVICE_NICKNAME} IDENTIFY " + ) + return None + + # If the account doesn't exist then insert into database + data_to_record = { + 'createdOn': self.Base.get_datetime(), 'account': fromuser, + 'nickname': user_obj.nickname, 'hostname': user_obj.hostname, 'vhost': user_obj.vhost, 'realname': user_obj.realname, 'email': email, + 'password': self.Base.crypt_password(password=password), 'level': 0 + } + + insert_to_db = self.Base.db_execute_query(f""" + INSERT INTO {self.Config.TABLE_CLIENT} + (createdOn, account, nickname, hostname, vhost, realname, email, password, level) + VALUES + (:createdOn, :account, :nickname, :hostname, :vhost, :realname, :email, :password, :level) + """, data_to_record) + + if insert_to_db.rowcount > 0: + self.Protocol.send_notice( + nick_from=dnickname, + nick_to=fromuser, + msg=f"You have register your nickname successfully" + ) - if user_obj is None: - self.Logs.error(f"Nickname ({fromuser}) doesn't exist, it is impossible to register this nickname") return None - # If the account already exist. - if self.Client.db_is_account_exist(fromuser): - self.Protocol.send_notice( - nick_from=dnickname, - nick_to=fromuser, - msg=f"Your account already exist, please try to login instead /msg {self.Config.SERVICE_NICKNAME} IDENTIFY " - ) - return None - - # If the account doesn't exist then insert into database - data_to_record = { - 'createdOn': self.Base.get_datetime(), 'account': fromuser, - 'nickname': user_obj.nickname, 'hostname': user_obj.hostname, 'vhost': user_obj.vhost, 'realname': user_obj.realname, 'email': email, - 'password': self.Base.crypt_password(password=password), 'level': 0 - } - - insert_to_db = self.Base.db_execute_query(f""" - INSERT INTO {self.Config.TABLE_CLIENT} - (createdOn, account, nickname, hostname, vhost, realname, email, password, level) - VALUES - (:createdOn, :account, :nickname, :hostname, :vhost, :realname, :email, :password, :level) - """, data_to_record) - - if insert_to_db.rowcount > 0: - self.Protocol.send_notice( - nick_from=dnickname, - nick_to=fromuser, - msg=f"You have register your nickname successfully" - ) - - return None + except ValueError as ve: + self.Logs.error(f"Value Error : {ve}") + self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" {self.Config.SERVICE_PREFIX}{command.upper()} ") case 'identify': - # Identify NICKNAME password - nickname = str(cmd[1]) - encrypted_password = self.Base.crypt_password(cmd[2]) - client_obj = self.Client.get_Client(nickname) - user_obj = self.User.get_User(fromuser) + # Identify ACCOUNT PASSWORD + try: + if len(cmd) < 3: + self.Protocol.send_notice( + nick_from=dnickname, + nick_to=fromuser, + msg=f'/msg {self.Config.SERVICE_NICKNAME} {command.upper()} ' + ) + return None + + account = str(cmd[1]) # account + encrypted_password = self.Base.crypt_password(cmd[2]) + user_obj = self.User.get_User(fromuser) + client_obj = self.Client.get_Client(user_obj.uid) + + if client_obj is not None: + self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"You are already logged in") + return None + + db_query = f"SELECT account FROM {self.Config.TABLE_CLIENT} WHERE account = :account AND password = :password" + db_param = {'account': account, 'password': encrypted_password} + exec_query = self.Base.db_execute_query( + db_query, + db_param + ) + result_query = exec_query.fetchone() + if result_query: + account = result_query[0] + self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"You are now logged in") + client = self.Loader.Definition.MClient( + uid=user_obj.uid, account=account, nickname=fromuser, + username=user_obj.username, realname=user_obj.realname, hostname=user_obj.hostname, umodes=user_obj.umodes, vhost=user_obj.vhost, + isWebirc=user_obj.isWebirc, isWebsocket=user_obj.isWebsocket, remote_ip=user_obj.remote_ip, score_connexion=user_obj.score_connexion, + geoip=user_obj.geoip, connexion_datetime=user_obj.connexion_datetime + ) + self.Client.insert(client) + self.Protocol.send_svs_mode(nickname=fromuser, user_mode='+r') + else: + self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Wrong password or account") - if client_obj is not None: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"You are already logged in") return None - db_query = f"SELECT account FROM {self.Config.TABLE_CLIENT} WHERE nickname = :nickname AND password = :password" - db_param = {'nickname': nickname, 'password': encrypted_password} - exec_query = self.Base.db_execute_query( - db_query, - db_param - ) - result_query = exec_query.fetchone() - if result_query: - account = result_query[0] - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"You are now logged in") - client = self.Loader.Definition.MClient( - uid=user_obj.uid, account=account, nickname=nickname, - username=user_obj.username, realname=user_obj.realname, hostname=user_obj.hostname, umodes=user_obj.umodes, vhost=user_obj.vhost, - isWebirc=user_obj.isWebirc, isWebsocket=user_obj.isWebsocket, remote_ip=user_obj.remote_ip, score_connexion=user_obj.score_connexion, - geoip=user_obj.geoip, connexion_datetime=user_obj.connexion_datetime - ) - self.Client.insert(client) - self.Protocol.send_svs_mode(nickname=nickname, user_mode='+r') - else: - self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"Wrong password or account") + except ValueError as ve: + self.Logs.error(f"Value Error: {ve}") + self.Protocol.send_notice( + nick_from=dnickname, + nick_to=fromuser, + msg=f'/msg {self.Config.SERVICE_NICKNAME} {command.upper()} ' + ) - return None + except Exception as err: + self.Logs.error(f"General Error: {err}") + + case 'logout': + try: + # LOGOUT + if len(cmd) < 2: + self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {command.upper()} ") + return None + + user_obj = self.User.get_User(fromuser) + if user_obj is None: + self.Logs.error(f"The User [{fromuser}] is not available in the database") + return None + + client_obj = self.Client.get_Client(user_obj.uid) + + if client_obj is None: + self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg="Nothing to logout. please login first") + return None + + self.Protocol.send_svs_mode(nickname=fromuser, user_mode='-r') + self.Client.delete(user_obj.uid) + self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"You have been logged out successfully") + + except ValueError as ve: + self.Logs.error(f"Value Error: {ve}") + self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {command.upper()} ") + except Exception as err: + self.Logs.error(f"General Error: {err}") + self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f"/msg {dnickname} {command.upper()} ") case 'help': @@ -1352,6 +1426,9 @@ class Irc: self.Base.shutdown() self.Base.execute_periodic_action() + for chan_name in self.Channel.UID_CHANNEL_DB: + self.Protocol.send_mode_chan(chan_name.name, '-l') + self.Protocol.send_notice( nick_from=dnickname, nick_to=fromuser, @@ -1403,7 +1480,7 @@ class Irc: current_version = self.Config.CURRENT_VERSION latest_version = self.Config.LATEST_VERSION - mods = ["core.config", "core.base", "core.classes.protocols.unreal6", "core.classes.protocol"] + mods = ["core.definition", "core.config", "core.base", "core.classes.protocols.unreal6", "core.classes.protocol"] mod_unreal6 = sys.modules['core.classes.protocols.unreal6'] mod_protocol = sys.modules['core.classes.protocol'] @@ -1426,7 +1503,7 @@ class Irc: config_dict: dict = self.Config.__dict__ for key, value in conf_bkp_dict.items(): - if config_dict[key] != value: + if config_dict[key] != value and key != 'COLORS': self.Protocol.send_priv_msg( nick_from=self.Config.SERVICE_NICKNAME, msg=f'[{key}]: {value} ==> {config_dict[key]}', diff --git a/mods/mod_command.py b/mods/mod_command.py index ac4ec32..f3b8874 100644 --- a/mods/mod_command.py +++ b/mods/mod_command.py @@ -1127,18 +1127,19 @@ class Command: case 'sapart': try: # .sapart nickname #channel - nickname = str(cmd[1]) - channel = str(cmd[2]) if len(cmd) < 3: self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname #channel") return None + nickname = str(cmd[1]) + channel = str(cmd[2]) + self.Protocol.send_sapart(nick_to_sapart=nickname, channel_name=channel) except KeyError as ke: self.Logs.error(ke) except Exception as err: self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f" /msg {dnickname} {command.upper()} nickname #channel") - self.Logs.warning(f'Unknown Error: {str(err)}') + self.Logs.error(f'Unknown Error: {str(err)}') case 'svsnick': try: diff --git a/mods/mod_defender.py b/mods/mod_defender.py index 3ebdf7b..e0efaed 100644 --- a/mods/mod_defender.py +++ b/mods/mod_defender.py @@ -1000,11 +1000,18 @@ class Defender(): case 'MODE': # ['...', ':001XSCU0Q', 'MODE', '#jail', '+b', '~security-group:unknown-users'] + # ['@unrealircd.org/...', ':001C0MF01', 'MODE', '#services', '+l', '1'] + channel = str(cmd[3]) mode = str(cmd[4]) group_to_check = str(cmd[5:]) group_to_unban = '~security-group:unknown-users' + if self.ModConfig.autolimit == 1: + if mode == '+l' or mode == '-l': + chan = self.Channel.get_Channel(channel) + self.Protocol.send2socket(f":{self.Config.SERVICE_ID} MODE {chan.name} +l {len(chan.uids) + self.ModConfig.autolimit_amount}") + if self.Config.SALON_JAIL == channel: if mode == '+b' and group_to_unban in group_to_check: self.Protocol.send2socket(f":{service_id} MODE {self.Config.SALON_JAIL} -b ~security-group:unknown-users") @@ -1709,6 +1716,9 @@ class Defender(): self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' {color_green if self.ModConfig.abuseipdb_scan == 1 else color_red}abuseipdb_scan{nogc} ==> {self.ModConfig.abuseipdb_scan}') self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' {color_green if self.ModConfig.freeipapi_scan == 1 else color_red}freeipapi_scan{nogc} ==> {self.ModConfig.freeipapi_scan}') self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' {color_green if self.ModConfig.cloudfilt_scan == 1 else color_red}cloudfilt_scan{nogc} ==> {self.ModConfig.cloudfilt_scan}') + self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' [{color_green if self.ModConfig.autolimit == 1 else color_red}Autolimit{nogc}] ==> {self.ModConfig.autolimit}') + self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' {color_green if self.ModConfig.autolimit == 1 else color_red}Autolimit Amount{nogc} ==> {self.ModConfig.autolimit_amount}') + self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' {color_green if self.ModConfig.autolimit == 1 else color_red}Autolimit Interval{nogc} ==> {self.ModConfig.autolimit_interval}') self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' [{color_green if self.ModConfig.flood == 1 else color_red}Flood{nogc}] ==> {self.ModConfig.flood}') self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=' flood_action ==> Coming soon') self.Protocol.send_notice(nick_from=dnickname, nick_to=fromuser, msg=f' flood_message ==> {self.ModConfig.flood_message}') diff --git a/version.json b/version.json index 1fba605..4a95ad2 100644 --- a/version.json +++ b/version.json @@ -1,5 +1,5 @@ { - "version": "6.1.0", + "version": "6.1.1", "requests": "2.32.3", "psutil": "6.0.0",