From 6b7fd16a44d3d6284616407304c4b9d445597429 Mon Sep 17 00:00:00 2001 From: adator <85586985+adator85@users.noreply.github.com> Date: Wed, 3 Sep 2025 22:01:52 +0200 Subject: [PATCH] Connectecting to inspircd --- .gitignore | 2 + core/base.py | 5 +- core/classes/protocol.py | 19 -- core/classes/protocols/factory.py | 28 ++ core/classes/protocols/inspircd.py | 68 ++++- core/classes/protocols/interface.py | 447 ++++++++++++++++++++++++++++ core/classes/protocols/unreal6.py | 23 +- core/classes/rehash.py | 10 +- core/irc.py | 36 +-- core/loader.py | 6 + core/logs.py | 2 +- core/utils.py | 8 +- defender.py | 13 +- 13 files changed, 585 insertions(+), 82 deletions(-) delete mode 100644 core/classes/protocol.py create mode 100644 core/classes/protocols/factory.py create mode 100644 core/classes/protocols/interface.py diff --git a/.gitignore b/.gitignore index 2428eb1..7977a52 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,7 @@ db/ logs/ __pycache__/ configuration.json +configuration_inspircd.json +configuration_unreal6.json *.log test.py \ No newline at end of file diff --git a/core/base.py b/core/base.py index 9f1f879..2755125 100644 --- a/core/base.py +++ b/core/base.py @@ -683,14 +683,13 @@ class Base: return False def decode_ip(self, ip_b64encoded: str) -> Optional[str]: - - binary_ip = b64decode(ip_b64encoded) try: + binary_ip = b64decode(ip_b64encoded) decoded_ip = ipaddress.ip_address(binary_ip) return decoded_ip.exploded except ValueError as ve: - self.logs.critical(f'This remote ip is not valid : {ve}') + self.logs.critical(f'This remote ip ({ip_b64encoded}) is not valid : {ve}') return None def encode_ip(self, remote_ip_address: str) -> Optional[str]: diff --git a/core/classes/protocol.py b/core/classes/protocol.py deleted file mode 100644 index e20ff20..0000000 --- a/core/classes/protocol.py +++ /dev/null @@ -1,19 +0,0 @@ -from typing import Literal, TYPE_CHECKING -from .protocols.unreal6 import Unrealircd6 -from .protocols.inspircd import Inspircd - -if TYPE_CHECKING: - from core.irc import Irc - -class Protocol: - - def __init__(self, protocol: Literal['unreal6','inspircd'], ircInstance: 'Irc'): - - self.Protocol = None - match protocol: - case 'unreal6': - self.Protocol: Unrealircd6 = Unrealircd6(ircInstance) - case 'inspircd': - self.Protocol: Inspircd = Inspircd(ircInstance) - case _: - self.Protocol: Unrealircd6 = Unrealircd6(ircInstance) diff --git a/core/classes/protocols/factory.py b/core/classes/protocols/factory.py new file mode 100644 index 0000000..32ac63a --- /dev/null +++ b/core/classes/protocols/factory.py @@ -0,0 +1,28 @@ +from typing import TYPE_CHECKING, Optional +from .unreal6 import Unrealircd6 +from .inspircd import Inspircd +from .interface import IProtocol + +if TYPE_CHECKING: + from core.irc import Irc + +class ProtocolFactorty: + + def __init__(self, uplink: 'Irc'): + self.__Config = uplink.Config + self.__uplink = uplink + + def get(self) -> Optional[IProtocol]: + + protocol = self.__Config.SERVEUR_PROTOCOL + + match protocol: + case 'unreal6': + self.__uplink.Logs.debug(f"[PROTOCOL] {protocol} has been loaded") + return Unrealircd6(self.__uplink) + case 'inspircd': + self.__uplink.Logs.debug(f"[PROTOCOL] {protocol} has been loaded") + return Inspircd(self.__uplink) + case _: + self.__uplink.Logs.critical(f"[PROTOCOL ERROR] This protocol name ({protocol} is not valid!)") + raise Exception("Unknown protocol!") diff --git a/core/classes/protocols/inspircd.py b/core/classes/protocols/inspircd.py index 33debaa..d41a9db 100644 --- a/core/classes/protocols/inspircd.py +++ b/core/classes/protocols/inspircd.py @@ -1,6 +1,7 @@ from re import match, findall from datetime import datetime -from typing import TYPE_CHECKING +import sys +from typing import TYPE_CHECKING, Optional from ssl import SSLEOFError, SSLError if TYPE_CHECKING: @@ -17,8 +18,32 @@ class Inspircd: self.__Utils = ircInstance.Loader.Utils self.__Logs = ircInstance.Loader.Logs + self.known_protocol: set[str] = {'SJOIN', 'UID', 'MD', 'QUIT', 'SQUIT', + 'EOS', 'PRIVMSG', 'MODE', 'UMODE2', + 'VERSION', 'REPUTATION', 'SVS2MODE', + 'SLOG', 'NICK', 'PART', 'PONG', 'SASL', 'PING', + 'PROTOCTL', 'SERVER', 'SMOD', 'TKL', 'NETINFO', + '006', '007', '018'} + self.__Logs.info(f"** Loading protocol [{__name__}]") + def get_ircd_protocol_poisition(self, cmd: list[str]) -> tuple[int, Optional[str]]: + """Get the position of known commands + + Args: + cmd (list[str]): The server response + + Returns: + tuple[int, Optional[str]]: The position and the command. + """ + for index, token in enumerate(cmd): + if token.upper() in self.known_protocol: + return index, token.upper() + + self.__Logs.debug(f"[IRCD LOGS] You need to handle this response: {cmd}") + + return (-1, None) + def send2socket(self, message: str, print_log: bool = True) -> None: """Envoit les commandes à envoyer au serveur. @@ -45,6 +70,8 @@ class Inspircd: self.__Logs.error(f"SSLError: {se} - {message}") except OSError as oe: self.__Logs.error(f"OSError: {oe} - {message}") + if oe.errno == 10053: + sys.exit(oe) except AttributeError as ae: self.__Logs.critical(f"Attribute Error: {ae}") @@ -175,7 +202,8 @@ class Inspircd: self.__Logs.error(f"The channel [{channel}] is not valid") return None - self.send2socket(f":{self.__Config.SERVEUR_ID} SJOIN {self.__Utils.get_unixtime()} {channel} + :{self.__Config.SERVICE_ID}") + # self.send2socket(f":{self.__Config.SERVEUR_ID} SJOIN {self.__Utils.get_unixtime()} {channel} + :{self.__Config.SERVICE_ID}") + self.send2socket(f":{self.__Config.SERVICE_ID} FJOIN {channel} 68") # Add defender to the channel uids list self.__Irc.Channel.insert(self.__Irc.Loader.Definition.MChannel(name=channel, uids=[self.__Config.SERVICE_ID])) @@ -481,32 +509,29 @@ class Inspircd: def on_uid(self, serverMsg: list[str]) -> None: """Handle uid message coming from the server - + [:] UID []+ : + [':97K', 'UID', '97KAAAAAB', '1756928055', 'adator_', '172.18.128.1', '172.18.128.1', '...', '...', '172.18.128.1', '1756928055', '+', ':...'] Args: serverMsg (list[str]): Original server message """ - # ['@s2s-md/geoip=cc=GB|cd=United\\sKingdom|asn=16276|asname=OVH\\sSAS;s2s-md/tls_cipher=TLSv1.3-TLS_CHACHA20_POLY1305_SHA256;s2s-md/creationtime=1721564601', - # ':001', 'UID', 'albatros', '0', '1721564597', 'albatros', 'vps-91b2f28b.vps.ovh.net', - # '001HB8G04', '0', '+iwxz', 'Clk-A62F1D18.vps.ovh.net', 'Clk-A62F1D18.vps.ovh.net', 'MyZBwg==', ':...'] try: - isWebirc = True if 'webirc' in serverMsg[0] else False isWebsocket = True if 'websocket' in serverMsg[0] else False - uid = str(serverMsg[8]) - nickname = str(serverMsg[3]) - username = str(serverMsg[6]) - hostname = str(serverMsg[7]) - umodes = str(serverMsg[10]) - vhost = str(serverMsg[11]) + uid = str(serverMsg[2]) + nickname = str(serverMsg[4]) + username = str(serverMsg[7]) + hostname = str(serverMsg[5]) + umodes = str(serverMsg[11]) + vhost = str(serverMsg[6]) if not 'S' in umodes: - remote_ip = self.__Base.decode_ip(str(serverMsg[13])) + remote_ip = self.__Base.decode_ip(str(serverMsg[9])) else: remote_ip = '127.0.0.1' # extract realname - realname = ' '.join(serverMsg[14:]).lstrip(':') + realname = ' '.join(serverMsg[12:]).lstrip(':') # Extract Geoip information pattern = r'^.*geoip=cc=(\S{2}).*$' @@ -540,7 +565,7 @@ class Inspircd: except IndexError as ie: self.__Logs.error(f"{__name__} - Index Error: {ie}") except Exception as err: - self.__Logs.error(f"{__name__} - General Error: {err}") + self.__Logs.error(f"{__name__} - General Error: {err}", exc_info=True) def on_server_ping(self, serverMsg: list[str]) -> None: """Send a PONG message to the server @@ -560,6 +585,17 @@ class Inspircd: except Exception as err: self.__Logs.error(f"{__name__} - General Error: {err}") + def on_server(self, serverMsg: list[str]) -> None: + """_summary_ + + Args: + serverMsg (list[str]): _description_ + """ + try: + ... + except Exception as err: + self.__Logs.error(f'General Error: {err}') + def on_version(self, serverMsg: list[str]) -> None: """Sending Server Version to the server diff --git a/core/classes/protocols/interface.py b/core/classes/protocols/interface.py new file mode 100644 index 0000000..17005c9 --- /dev/null +++ b/core/classes/protocols/interface.py @@ -0,0 +1,447 @@ +from abc import ABC, abstractmethod +from typing import Optional, TYPE_CHECKING + +if TYPE_CHECKING: + from core.classes.sasl import Sasl + from core.definition import MClient, MSasl + +class IProtocol(ABC): + + @abstractmethod + def get_ircd_protocol_poisition(self, cmd: list[str]) -> tuple[int, Optional[str]]: + """Get the position of known commands + + Args: + cmd (list[str]): The server response + + Returns: + tuple[int, Optional[str]]: The position and the command. + """ + + @abstractmethod + def send2socket(self, message: str, print_log: bool = True) -> None: + """Envoit les commandes à envoyer au serveur. + + Args: + string (Str): contient la commande à envoyer au serveur. + """ + + @abstractmethod + def send_priv_msg(self, nick_from: str, msg: str, channel: str = None, nick_to: str = None): + """Sending PRIVMSG to a channel or to a nickname by batches + could be either channel or nickname not both together + Args: + msg (str): The message to send + nick_from (str): The sender nickname + channel (str, optional): The receiver channel. Defaults to None. + nick_to (str, optional): The reciever nickname. Defaults to None. + """ + + @abstractmethod + def send_notice(self, nick_from: str, nick_to: str, msg: str) -> None: + """Sending NOTICE by batches + + Args: + msg (str): The message to send to the server + nick_from (str): The sender Nickname + nick_to (str): The reciever nickname + """ + + @abstractmethod + def send_link(self) -> None: + """Créer le link et envoyer les informations nécessaires pour la + connexion au serveur. + """ + + @abstractmethod + def send_gline(self, nickname: str, hostname: str, set_by: str, expire_timestamp: int, set_at_timestamp: int, reason: str) -> None: + """_summary_ + + Args: + nickname (str): _description_ + hostname (str): _description_ + set_by (str): _description_ + expire_timestamp (int): _description_ + set_at_timestamp (int): _description_ + reason (str): _description_ + """ + + @abstractmethod + def send_set_nick(self, newnickname: str) -> None: + """Change nickname of the server + \n This method will also update the User object + Args: + newnickname (str): New nickname of the server + """ + + @abstractmethod + def send_squit(self, server_id: str, server_link: str, reason: str) -> None: + """_summary_ + + Args: + server_id (str): _description_ + server_link (str): _description_ + reason (str): _description_ + """ + + @abstractmethod + def send_ungline(self, nickname:str, hostname: str) -> None: + """_summary_ + + Args: + nickname (str): _description_ + hostname (str): _description_ + """ + + @abstractmethod + def send_kline(self, nickname: str, hostname: str, set_by: str, expire_timestamp: int, set_at_timestamp: int, reason: str) -> None: + """_summary_ + + Args: + nickname (str): _description_ + hostname (str): _description_ + set_by (str): _description_ + expire_timestamp (int): _description_ + set_at_timestamp (int): _description_ + reason (str): _description_ + """ + + @abstractmethod + def send_unkline(self, nickname:str, hostname: str) -> None: + """_summary_ + + Args: + nickname (str): _description_ + hostname (str): _description_ + """ + + @abstractmethod + def send_sjoin(self, channel: str) -> None: + """Server will join a channel with pre defined umodes + + Args: + channel (str): Channel to join + """ + + @abstractmethod + def send_sapart(self, nick_to_sapart: str, channel_name: str) -> None: + """_summary_ + + Args: + from_nick (str): _description_ + nick_to (str): _description_ + channel_name (str): _description_ + """ + + @abstractmethod + def send_sajoin(self, nick_to_sajoin: str, channel_name: str) -> None: + """_summary_ + + Args: + nick_to_sajoin (str): _description_ + channel_name (str): _description_ + """ + + @abstractmethod + def send_svspart(self, nick_to_part: str, channels: list[str], reason: str) -> None: + """_summary_ + + Args: + nick_to_part (str): _description_ + channels (list[str]): _description_ + reason (str): _description_ + """ + + @abstractmethod + def send_svsjoin(self, nick_to_part: str, channels: list[str], keys: list[str]) -> None: + """_summary_ + + Args: + nick_to_part (str): _description_ + channels (list[str]): _description_ + keys (list[str]): _description_ + """ + + @abstractmethod + def send_svsmode(self, nickname: str, user_mode: str) -> None: + """_summary_ + + Args: + nickname (str): _description_ + user_mode (str): _description_ + """ + + @abstractmethod + def send_svs2mode(self, nickname: str, user_mode: str) -> None: + """_summary_ + + Args: + nickname (str): _description_ + user_mode (str): _description_ + """ + + @abstractmethod + def send_svslogin(self, client_uid: str, user_account: str) -> None: + """Log a client into his account. + + Args: + client_uid (str): Client UID + user_account (str): The account of the user + """ + + @abstractmethod + def send_svslogout(self, client_obj: 'MClient') -> None: + """Logout a client from his account + + Args: + client_uid (str): The Client UID + """ + + @abstractmethod + def send_quit(self, uid: str, reason: str, print_log: True) -> None: + """Send quit message + - Delete uid from User object + - Delete uid from Reputation object + + Args: + uidornickname (str): The UID or the Nickname + reason (str): The reason for the quit + """ + + @abstractmethod + def send_uid(self, nickname:str, username: str, hostname: str, uid:str, umodes: str, vhost: str, remote_ip: str, realname: str, print_log: bool = True) -> None: + """Send UID to the server + - Insert User to User Object + Args: + nickname (str): Nickname of the client + username (str): Username of the client + hostname (str): Hostname of the client you want to create + uid (str): UID of the client you want to create + umodes (str): umodes of the client you want to create + vhost (str): vhost of the client you want to create + remote_ip (str): remote_ip of the client you want to create + realname (str): realname of the client you want to create + print_log (bool, optional): print logs if true. Defaults to True. + """ + + @abstractmethod + def send_join_chan(self, uidornickname: str, channel: str, password: str = None, print_log: bool = True) -> None: + """Joining a channel + + Args: + uidornickname (str): UID or nickname that need to join + channel (str): channel to join + password (str, optional): The password of the channel to join. Default to None + print_log (bool, optional): Write logs. Defaults to True. + """ + + @abstractmethod + def send_part_chan(self, uidornickname:str, channel: str, print_log: bool = True) -> None: + """Part from a channel + + Args: + uidornickname (str): UID or nickname that need to join + channel (str): channel to join + print_log (bool, optional): Write logs. Defaults to True. + """ + + @abstractmethod + def send_mode_chan(self, channel_name: str, channel_mode: str) -> None: + """_summary_ + + Args: + channel_name (str): _description_ + channel_mode (str): _description_ + """ + + @abstractmethod + def send_raw(self, raw_command: str) -> None: + """_summary_ + + Args: + raw_command (str): _description_ + """ + + ##################### + # HANDLE EVENTS # + ##################### + + @abstractmethod + def on_svs2mode(self, serverMsg: list[str]) -> None: + """Handle svs2mode coming from a server + >>> [':00BAAAAAG', 'SVS2MODE', '001U01R03', '-r'] + + Args: + serverMsg (list[str]): Original server message + """ + + @abstractmethod + def on_mode(self, serverMsg: list[str]) -> None: + """Handle mode coming from a server + + Args: + serverMsg (list[str]): Original server message + """ + + @abstractmethod + def on_umode2(self, serverMsg: list[str]) -> None: + """Handle umode2 coming from a server + >>> [':adator_', 'UMODE2', '-i'] + + Args: + serverMsg (list[str]): Original server message + """ + + @abstractmethod + def on_quit(self, serverMsg: list[str]) -> None: + """Handle quit coming from a server + + Args: + serverMsg (list[str]): Original server message + """ + + @abstractmethod + def on_squit(self, serverMsg: list[str]) -> None: + """Handle squit coming from a server + + Args: + serverMsg (list[str]): Original server message + """ + + @abstractmethod + def on_protoctl(self, serverMsg: list[str]) -> None: + """Handle protoctl coming from a server + + Args: + serverMsg (list[str]): Original server message + """ + + @abstractmethod + def on_nick(self, serverMsg: list[str]) -> None: + """Handle nick coming from a server + new nickname + + Args: + serverMsg (list[str]): Original server message + """ + + @abstractmethod + def on_sjoin(self, serverMsg: list[str]) -> None: + """Handle sjoin coming from a server + + Args: + serverMsg (list[str]): Original server message + """ + + @abstractmethod + def on_part(self, serverMsg: list[str]) -> None: + """Handle part coming from a server + + Args: + serverMsg (list[str]): Original server message + """ + + @abstractmethod + def on_eos(self, serverMsg: list[str]) -> None: + """Handle EOS coming from a server + + Args: + serverMsg (list[str]): Original server message + """ + + @abstractmethod + def on_reputation(self, serverMsg: list[str]) -> None: + """Handle REPUTATION coming from a server + + Args: + serverMsg (list[str]): Original server message + """ + + @abstractmethod + def on_uid(self, serverMsg: list[str]) -> None: + """Handle uid message coming from the server + + Args: + serverMsg (list[str]): Original server message + """ + + @abstractmethod + def on_privmsg(self, serverMsg: list[str]) -> None: + """Handle PRIVMSG message coming from the server + + Args: + serverMsg (list[str]): Original server message + """ + + @abstractmethod + def on_server_ping(self, serverMsg: list[str]) -> None: + """Send a PONG message to the server + + Args: + serverMsg (list[str]): List of str coming from the server + """ + + @abstractmethod + def on_server(self, serverMsg: list[str]) -> None: + """_summary_ + + Args: + serverMsg (list[str]): _description_ + """ + + @abstractmethod + def on_version(self, serverMsg: list[str]) -> None: + """Sending Server Version to the server + + Args: + serverMsg (list[str]): List of str coming from the server + """ + + @abstractmethod + def on_time(self, serverMsg: list[str]) -> None: + """Sending TIME answer to a requestor + + Args: + serverMsg (list[str]): List of str coming from the server + """ + + @abstractmethod + def on_ping(self, serverMsg: list[str]) -> None: + """Sending a PING answer to requestor + + Args: + serverMsg (list[str]): List of str coming from the server + """ + + @abstractmethod + def on_version_msg(self, serverMsg: list[str]) -> None: + """Handle version coming from the server + \n ex. /version Defender + Args: + serverMsg (list[str]): Original message from the server + """ + + @abstractmethod + def on_smod(self, serverMsg: list[str]) -> None: + """Handle SMOD message coming from the server + + Args: + serverMsg (list[str]): Original server message + """ + + @abstractmethod + def on_sasl(self, serverMsg: list[str], psasl: 'Sasl') -> Optional['MSasl']: + """Handle SASL coming from a server + + Args: + serverMsg (list[str]): Original server message + psasl (Sasl): The SASL process object + """ + + @abstractmethod + def on_md(self, serverMsg: list[str]) -> None: + """Handle MD responses + [':001', 'MD', 'client', '001MYIZ03', 'certfp', ':d1235648...'] + Args: + serverMsg (list[str]): The server reply + """ diff --git a/core/classes/protocols/unreal6.py b/core/classes/protocols/unreal6.py index 14c7542..33581bb 100644 --- a/core/classes/protocols/unreal6.py +++ b/core/classes/protocols/unreal6.py @@ -4,6 +4,7 @@ from datetime import datetime from typing import TYPE_CHECKING, Optional from ssl import SSLEOFError, SSLError +from core.classes.protocols.interface import IProtocol from core.utils import tr if TYPE_CHECKING: @@ -11,7 +12,7 @@ if TYPE_CHECKING: from core.classes.sasl import Sasl from core.definition import MClient, MSasl -class Unrealircd6: +class Unrealircd6(IProtocol): def __init__(self, ircInstance: 'Irc'): self.name = 'UnrealIRCD-6' @@ -200,6 +201,7 @@ class Unrealircd6: self.send2socket(f":{server_id} PROTOCTL SID={server_id}") self.send2socket(f":{server_id} PROTOCTL BOOTED={unixtime}") self.send2socket(f":{server_id} SERVER {link} 1 :{info}") + self.send2socket("EOS") self.send2socket(f":{server_id} {nickname} :Reserved for services") self.send2socket(f":{server_id} UID {nickname} 1 {unixtime} {username} {host} {service_id} * {smodes} * * fwAAAQ== :{realname}") self.send_sjoin(chan) @@ -1035,15 +1037,6 @@ class Unrealircd6: ) return None - # if not arg[0].lower() in self.__Irc.module_commands_list: - # self.__Logs.debug(f"This command {arg[0]} is not available") - # self.send_notice( - # nick_from=self.__Config.SERVICE_NICKNAME, - # nick_to=user_trigger, - # msg=f"This command [{self.__Config.COLORS.bold}{arg[0]}{self.__Config.COLORS.bold}] is not available" - # ) - # return None - cmd_to_send = convert_to_string.replace(':','') self.__Base.log_cmd(user_trigger, cmd_to_send) @@ -1106,7 +1099,6 @@ class Unrealircd6: serverMsg (list[str]): List of str coming from the server """ try: - # pong = str(serverMsg[1]).replace(':','') self.send2socket(f"PONG :{pong}", print_log=False) @@ -1115,6 +1107,11 @@ class Unrealircd6: self.__Logs.error(f"{__name__} - General Error: {err}") def on_server(self, serverMsg: list[str]) -> None: + """_summary_ + + Args: + serverMsg (list[str]): _description_ + """ try: # ['SERVER', 'irc.local.org', '1', ':U6100-Fhn6OoE-001', 'Local', 'Server'] sCopy = serverMsg.copy() @@ -1177,7 +1174,7 @@ class Unrealircd6: Args: serverMsg (list[str]): List of str coming from the server """ - # ['@unrealircd.org/userhost=StatServ@stats.deb.biz.st;draft/bot;bot;msgid=ehfAq3m2yjMjhgWEfi1UCS;time=2024-10-26T13:49:06.299Z', ':001INC60B', 'PRIVMSG', '12ZAAAAAB', ':\x01PING', '762382207\x01'] + # ['@unrealircd.org/...', ':001INC60B', 'PRIVMSG', '12ZAAAAAB', ':\x01PING', '762382207\x01'] # Réponse a un CTCP VERSION try: @@ -1186,6 +1183,7 @@ class Unrealircd6: arg = serverMsg[4].replace(':', '') if nickname is None: + self.__Logs.debug(serverMsg) return None if arg == '\x01PING': @@ -1199,6 +1197,7 @@ class Unrealircd6: nick_to=nickname, msg=f"\x01PING {ping_response} secs\x01" ) + self.__Logs.debug(serverMsg) return None except Exception as err: diff --git a/core/classes/rehash.py b/core/classes/rehash.py index bc7dfa6..72a2465 100644 --- a/core/classes/rehash.py +++ b/core/classes/rehash.py @@ -3,7 +3,6 @@ import sys import time from typing import TYPE_CHECKING import socket -from core.classes.protocol import Protocol if TYPE_CHECKING: from core.irc import Irc @@ -15,9 +14,10 @@ REHASH_MODULES = [ 'core.classes.config', 'core.base', 'core.classes.commands', + 'core.classes.protocols.interface', + 'core.classes.protocols.factory', 'core.classes.protocols.unreal6', - 'core.classes.protocols.inspircd', - 'core.classes.protocol' + 'core.classes.protocols.inspircd' ] @@ -36,7 +36,7 @@ def restart_service(uplink: 'Irc', reason: str = "Restarting with no reason!") - # Reload configuration uplink.Config = uplink.Loader.ConfModule.Configuration(uplink.Loader).get_config_model() uplink.Base = uplink.Loader.BaseModule.Base(uplink.Loader) - uplink.Protocol = Protocol(uplink.Config.SERVEUR_PROTOCOL, uplink.ircObject).Protocol + uplink.Protocol = uplink.Loader.PFactory.get() uplink.Logs.debug(f'[{uplink.Config.SERVICE_NICKNAME} RESTART]: Reloading configuration!') uplink.Protocol.send_squit(server_id=uplink.Config.SERVEUR_ID, server_link=uplink.Config.SERVEUR_LINK, reason="Defender Power off") @@ -106,7 +106,7 @@ def rehash_service(uplink: 'Irc', nickname: str) -> None: uplink.Commands.DB_COMMANDS = uplink.Settings.get_cache('db_commands') uplink.Base = uplink.Loader.BaseModule.Base(uplink.Loader) - uplink.Protocol = Protocol(uplink.Config.SERVEUR_PROTOCOL, uplink.ircObject).Protocol + uplink.Protocol = uplink.Loader.PFactory.get() # Reload Service modules for module in uplink.ModuleUtils.model_get_loaded_modules().copy(): diff --git a/core/irc.py b/core/irc.py index 19676f1..689e8e9 100644 --- a/core/irc.py +++ b/core/irc.py @@ -7,12 +7,12 @@ from ssl import SSLSocket from datetime import datetime, timedelta from typing import TYPE_CHECKING, Any, Optional, Union from core.classes import rehash -from core.loader import Loader -from core.classes.protocol import Protocol +from core.classes.protocols.interface import IProtocol from core.utils import tr if TYPE_CHECKING: from core.definition import MSasl + from core.loader import Loader class Irc: _instance = None @@ -24,7 +24,7 @@ class Irc: return cls._instance - def __init__(self, loader: Loader) -> 'Irc': + def __init__(self, loader: 'Loader'): # Loader class self.Loader = loader @@ -135,7 +135,7 @@ class Irc: ############################################## # CONNEXION IRC # ############################################## - def init_irc(self, ircInstance: 'Irc') -> None: + def init_irc(self) -> None: """Create a socket and connect to irc server Args: @@ -143,8 +143,8 @@ class Irc: """ try: self.init_service_user() - self.Utils.create_socket(ircInstance) - self.__connect_to_irc(ircInstance) + self.Utils.create_socket(self) + self.__connect_to_irc() except AssertionError as ae: self.Logs.critical(f'Assertion error: {ae}') @@ -161,23 +161,20 @@ class Irc: )) return None - def __connect_to_irc(self, ircInstance: 'Irc') -> None: + def __connect_to_irc(self) -> None: try: self.init_service_user() - self.ircObject = ircInstance # créer une copie de l'instance Irc - self.Protocol = Protocol( - protocol=self.Config.SERVEUR_PROTOCOL, - ircInstance=self.ircObject - ).Protocol + self.Protocol: 'IProtocol' = self.Loader.PFactory.get() self.Protocol.send_link() # Etablir le link en fonction du protocol choisi self.signal = True # Une variable pour initier la boucle infinie self.join_saved_channels() # Join existing channels - self.ModuleUtils.db_load_all_existing_modules(self) + time.sleep(3) + # self.ModuleUtils.db_load_all_existing_modules(self) while self.signal: try: if self.Config.DEFENDER_RESTART == 1: - rehash.restart_service(self.ircObject) + rehash.restart_service(self) # 4072 max what the socket can grab buffer_size = self.IrcSocket.getsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF) @@ -202,9 +199,11 @@ class Irc: self.Logs.error(f"SSLEOFError __connect_to_irc: {soe} - {data}") except ssl.SSLError as se: self.Logs.error(f"SSLError __connect_to_irc: {se} - {data}") - sys.exit(1) + sys.exit(-1) except OSError as oe: - self.Logs.error(f"SSLError __connect_to_irc: {oe} - {data}") + self.Logs.error(f"SSLError __connect_to_irc: {oe} {oe.errno}") + if oe.errno == 10053: + sys.exit(-1) except (socket.error, ConnectionResetError): self.Logs.debug("Connexion reset") @@ -790,8 +789,9 @@ class Irc: if admin_obj: self.Protocol.send_priv_msg(nick_from=dnickname, - msg=f"[ {GREEN}{str(current_command).upper()}{NOGC} ] - You are already connected to {dnickname}", + msg=f"[ {GREEN}{str(current_command).upper()}{NOGC} ] - {fromuser} is already connected to {dnickname}", channel=dchanlog) + self.Protocol.send_notice(dnickname, fromuser, tr("You are already connected to %s", dnickname)) return None mes_donnees = {'user': user_to_log, 'password': self.Loader.Utils.hash_password(password)} @@ -1179,7 +1179,7 @@ class Irc: self.Config.DEFENDER_INIT = 1 # set init to 1 saying that the service will be re initiated case 'rehash': - rehash.rehash_service(self.ircObject, fromuser) + rehash.rehash_service(self, fromuser) return None case 'show_modules': diff --git a/core/loader.py b/core/loader.py index 33f0c3b..b92f2d6 100644 --- a/core/loader.py +++ b/core/loader.py @@ -8,6 +8,8 @@ import core.base as base_mod import core.module as module_mod import core.classes.commands as commands_mod import core.classes.config as conf_mod +import core.irc as irc +import core.classes.protocols.factory as factory class Loader: @@ -63,4 +65,8 @@ class Loader: self.Sasl: sasl.Sasl = sasl.Sasl(self) + self.Irc: irc.Irc = irc.Irc(self) + + self.PFactory: factory.ProtocolFactorty = factory.ProtocolFactorty(self.Irc) + self.Logs.debug(self.Utils.tr("Loader %s success", __name__)) diff --git a/core/logs.py b/core/logs.py index 19f2e62..fbf02a7 100644 --- a/core/logs.py +++ b/core/logs.py @@ -15,7 +15,7 @@ class ServiceLogging: self.SERVER_PREFIX = None self.LOGGING_CONSOLE = True - self.LOG_FILTERS: list[str] = ['PING', f":{self.SERVER_PREFIX}auth", "['PASS'"] + self.LOG_FILTERS: list[str] = ["PING", f":{self.SERVER_PREFIX}auth", "['PASS'"] self.file_handler = None self.stdout_handler = None diff --git a/core/utils.py b/core/utils.py index bf4f350..bbfca6f 100644 --- a/core/utils.py +++ b/core/utils.py @@ -36,7 +36,7 @@ def tr(message: str, *args) -> str: is_args_available = True if args else False g = global_settings try: - # Access to user object ==> global_instance.get_user_option + # Access to admin object client_language = g.current_admin.language if g.current_admin else g.global_lang if count_args != count_placeholder: @@ -56,7 +56,7 @@ def tr(message: str, *args) -> str: return message % args if is_args_available else message except KeyError as ke: - g.global_logger.error(f"Key Error: {ke}") + g.global_logger.error(f"KeyError: {ke}") return message % args if is_args_available else message except Exception as err: @@ -143,6 +143,8 @@ def create_socket(uplink: 'Irc') -> None: uplink.Logs.critical(f"[OS Error]: {oe}") if 'connection refused' in str(oe).lower(): sys.exit(oe) + if oe.errno == 10053: + sys.exit(oe) except AttributeError as ae: uplink.Logs.critical(f"AttributeError: {ae}") @@ -241,4 +243,4 @@ def hide_sensitive_data(srvmsg: list[str]) -> list[str]: return srv_msg except ValueError: - return srvmsg \ No newline at end of file + return srvmsg diff --git a/defender.py b/defender.py index ace6ab3..6b5e1bc 100644 --- a/defender.py +++ b/defender.py @@ -11,14 +11,17 @@ from core import installation try: - installation.Install() + # installation.Install() from core.loader import Loader - from core.irc import Irc - ircInstance = Irc(Loader()) - ircInstance.init_irc(ircInstance) + loader = Loader() + loader.Irc.init_irc() + # from core.irc import Irc + # ircInstance = Irc(Loader()) + # ircInstance.init_irc(ircInstance) except AssertionError as ae: print(f'Assertion Error -> {ae}') except KeyboardInterrupt as k: - ircInstance.Base.execute_periodic_action() \ No newline at end of file + # ircInstance.Base.execute_periodic_action() + ... \ No newline at end of file