many updates

This commit is contained in:
adator
2024-11-02 23:22:36 +01:00
parent 9d9ede0e80
commit cbae3dce96
12 changed files with 488 additions and 387 deletions

View File

@@ -202,11 +202,15 @@ class Base:
def replace_filter(self, record: logging.LogRecord) -> bool:
response = True
filter: list[str] = ['PING', f":{self.Config.SERVICE_PREFIX}auth"]
# record.msg = record.getMessage().replace("PING", "[REDACTED]")
if self.Settings.CONSOLE:
print(record.getMessage())
response = False if "PING" in record.getMessage() else True
response = False if f":{self.Config.SERVICE_PREFIX}auth" in record.getMessage() else True
for f in filter:
if f in record.getMessage():
response = False
return response # Retourne True pour permettre l'affichage du message
@@ -402,59 +406,6 @@ class Base:
return True
# def db_query_channel(self, action: Literal['add','del'], module_name: str, channel_name: str) -> bool:
# """You can add a channel or delete a channel.
# Args:
# action (Literal['add','del']): Action on the database
# module_name (str): The module name (mod_test)
# channel_name (str): The channel name (With #)
# Returns:
# bool: True if action done
# """
# try:
# channel_name = channel_name.lower() if self.Is_Channel(channel_name) else None
# core_table = self.Config.TABLE_CHANNEL
# if not channel_name:
# self.logs.warning(f'The channel [{channel_name}] is not correct')
# return False
# match action:
# case 'add':
# mes_donnees = {'module_name': module_name, 'channel_name': channel_name}
# response = self.db_execute_query(f"SELECT id FROM {core_table} WHERE module_name = :module_name AND channel_name = :channel_name", mes_donnees)
# isChannelExist = response.fetchone()
# if isChannelExist is None:
# mes_donnees = {'datetime': self.get_datetime(), 'channel_name': channel_name, 'module_name': module_name}
# insert = self.db_execute_query(f"INSERT INTO {core_table} (datetime, channel_name, module_name) VALUES (:datetime, :channel_name, :module_name)", mes_donnees)
# if insert.rowcount:
# self.logs.debug(f'New channel added: channel={channel_name} / module_name={module_name}')
# return True
# else:
# return False
# pass
# case 'del':
# mes_donnes = {'channel_name': channel_name, 'module_name': module_name}
# response = self.db_execute_query(f"DELETE FROM {core_table} WHERE channel_name = :channel_name AND module_name = :module_name", mes_donnes)
# if response.rowcount > 0:
# self.logs.debug(f'Channel deleted: channel={channel_name} / module: {module_name}')
# return True
# else:
# return False
# case _:
# return False
# except Exception as err:
# self.logs.error(err)
def db_create_first_admin(self) -> None:
user = self.db_execute_query(f"SELECT id FROM {self.Config.TABLE_ADMIN}")

View File

@@ -81,6 +81,7 @@ class Channel:
def delete_user_from_channel(self, chan_name: str, uid:str) -> bool:
try:
result = False
chan_name = chan_name.lower()
for record in self.UID_CHANNEL_DB:
if record.name == chan_name:

View File

@@ -55,20 +55,15 @@ class Clone:
Returns:
bool: True if deleted
"""
result = False
for record in self.UID_CLONE_DB:
if record.nickname == uidornickname or record.uid == uidornickname:
# If the user exist then remove and return True and do not go further
self.UID_CLONE_DB.remove(record)
result = True
# self.Logs.debug(f'The clone ({record.nickname}) has been deleted')
return result
cloneObj = self.get_Clone(uidornickname=uidornickname)
if not result:
self.Logs.critical(f'The UID or Nickname {uidornickname} was not deleted')
if cloneObj is None:
return False
return result
self.UID_CLONE_DB.remove(cloneObj)
return True
def exists(self, nickname: str) -> bool:
"""Check if the nickname exist
@@ -113,11 +108,15 @@ class Clone:
Returns:
Union[MClone, None]: Return MClone object or None
"""
cloneObj = None
for clone in self.UID_CLONE_DB:
if clone.uid == uidornickname:
return clone
cloneObj = clone
if clone.nickname == uidornickname:
return clone
cloneObj = clone
return cloneObj
def get_uid(self, uidornickname: str) -> Union[str, None]:
"""Get the UID of the clone starting from the UID or the Nickname

View File

@@ -1,3 +1,5 @@
from re import match, findall
from datetime import datetime
from typing import TYPE_CHECKING
from ssl import SSLEOFError, SSLError
@@ -23,12 +25,12 @@ class Unrealircd6:
if print_log:
self.__Base.logs.debug(f'<< {message}')
except UnicodeDecodeError:
self.__Base.logs.error(f'Decode Error try iso-8859-1 - message: {message}')
self.__Irc.IrcSocket.send(f"{message}\r\n".encode(self.__Config.SERVEUR_CHARSET[0],'replace'))
except UnicodeEncodeError:
self.__Base.logs.error(f'Encode Error try iso-8859-1 - message: {message}')
self.__Irc.IrcSocket.send(f"{message}\r\n".encode(self.__Config.SERVEUR_CHARSET[0],'replace'))
except UnicodeDecodeError as ude:
self.__Base.logs.error(f'Decode Error try iso-8859-1 - {ude} - {message}')
self.__Irc.IrcSocket.send(f"{message}\r\n".encode(self.__Config.SERVEUR_CHARSET[1],'replace'))
except UnicodeEncodeError as uee:
self.__Base.logs.error(f'Encode Error try iso-8859-1 - {uee} - {message}')
self.__Irc.IrcSocket.send(f"{message}\r\n".encode(self.__Config.SERVEUR_CHARSET[1],'replace'))
except AssertionError as ae:
self.__Base.logs.warning(f'Assertion Error {ae} - message: {message}')
except SSLEOFError as soe:
@@ -150,6 +152,9 @@ class Unrealircd6:
def squit(self, server_id: str, server_link: str, reason: str) -> None:
if not reason:
reason = 'Service Shutdown'
self.send2socket(f":{server_id} SQUIT {server_link} :{reason}")
return None
@@ -168,13 +173,81 @@ class Unrealircd6:
def sjoin(self, channel: str) -> None:
if not self.__Irc.Channel.Is_Channel(channel):
self.__Base.logs.error(f"The channel [{channel}] is not valid")
return None
self.send2socket(f":{self.__Config.SERVEUR_ID} SJOIN {self.__Base.get_unixtime()} {channel} + :{self.__Config.SERVICE_ID}")
# Add defender to the channel uids list
self.__Irc.Channel.insert(self.__Irc.Loader.Definition.MChannel(name=channel, uids=[self.__Config.SERVICE_ID]))
return None
def join(self, uidornickname: str, channel: str, password: str = None, print_log: bool = True) -> None:
def sendQuit(self, uid: str, reason: str, print_log: True) -> None:
"""Send quit message
Args:
uidornickname (str): The UID or the Nickname
reason (str): The reason for the quit
"""
userObj = self.__Irc.User.get_User(uidornickname=uid)
cloneObj = self.__Irc.Clone.get_Clone(uidornickname=uid)
reputationObj = self.__Irc.Reputation.get_Reputation(uidornickname=uid)
if not userObj is None:
self.send2socket(f":{userObj.uid} QUIT :{reason}", print_log=print_log)
self.__Irc.User.delete(userObj.uid)
if not cloneObj is None:
self.__Irc.Clone.delete(cloneObj.uid)
if not reputationObj is None:
self.__Irc.Reputation.delete(reputationObj.uid)
if not self.__Irc.Channel.delete_user_from_all_channel(uid):
self.__Base.logs.error(f"The UID [{uid}] has not been deleted from all channels")
return None
def sendUID(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
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.
"""
# {self.Config.SERVEUR_ID} UID
# {clone.nickname} 1 {self.Base.get_unixtime()} {clone.username} {clone.hostname} {clone.uid} * {clone.umodes} {clone.vhost} * {self.Base.encode_ip(clone.remote_ip)} :{clone.realname}
try:
unixtime = self.__Base.get_unixtime()
encoded_ip = self.__Base.encode_ip(remote_ip)
# Create the user
self.__Irc.User.insert(
self.__Irc.Loader.Definition.MUser(
uid=uid, nickname=nickname, username=username,
realname=realname,hostname=hostname, umodes=umodes,
vhost=vhost, remote_ip=remote_ip
)
)
uid_msg = f":{self.__Config.SERVEUR_ID} UID {nickname} 1 {unixtime} {username} {hostname} {uid} * {umodes} {vhost} * {encoded_ip} :{realname}"
self.send2socket(uid_msg, print_log=print_log)
return None
except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}")
def sendChanJoin(self, uidornickname: str, channel: str, password: str = None, print_log: bool = True) -> None:
"""Joining a channel
Args:
@@ -191,6 +264,7 @@ class Unrealircd6:
return None
if not self.__Irc.Channel.Is_Channel(channel):
self.__Base.logs.error(f"The channel [{channel}] is not valid")
return None
self.send2socket(f":{userObj.uid} JOIN {channel} {passwordChannel}", print_log=print_log)
@@ -199,7 +273,7 @@ class Unrealircd6:
self.__Irc.Channel.insert(self.__Irc.Loader.Definition.MChannel(name=channel, uids=[userObj.uid]))
return None
def part(self, uidornickname:str, channel: str, print_log: bool = True) -> None:
def sendChanPart(self, uidornickname:str, channel: str, print_log: bool = True) -> None:
"""Part from a channel
Args:
@@ -211,9 +285,11 @@ class Unrealircd6:
userObj = self.__Irc.User.get_User(uidornickname)
if userObj is None:
self.__Base.logs.error(f"The user [{uidornickname}] is not valid")
return None
if not self.__Irc.Channel.Is_Channel(channel):
self.__Base.logs.error(f"The channel [{channel}] is not valid")
return None
self.send2socket(f":{userObj.uid} PART {channel}", print_log=print_log)
@@ -228,6 +304,214 @@ class Unrealircd6:
return None
def on_umode2(self, serverMsg: list[str]) -> None:
"""Handle umode2 coming from a server
Args:
serverMsg (list[str]): Original server message
"""
try:
# [':adator_', 'UMODE2', '-iwx']
userObj = self.__Irc.User.get_User(str(serverMsg[0]).lstrip(':'))
userMode = serverMsg[2]
if userObj is None: # If user is not created
return None
# save previous user modes
old_umodes = userObj.umodes
# TODO : User object should be able to update user modes
if self.__Irc.User.update_mode(userObj.uid, userMode):
return None
# self.__Base.logs.debug(f"Updating user mode for [{userObj.nickname}] [{old_umodes}] => [{userObj.umodes}]")
return None
except IndexError as ie:
self.__Base.logs.error(f"{__name__} - Index Error: {ie}")
except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}")
def on_quit(self, serverMsg: list[str]) -> None:
"""Handle quit coming from a server
Args:
serverMsg (list[str]): Original server message
"""
try:
# ['@unrealircd.org/userhost=...@192.168.1.10;unrealircd.org/userip=...@192.168.1.10;msgid=CssUrV08BzekYuq7BfvPHn;time=2024-11-02T15:03:33.182Z', ':001JKNY0N', 'QUIT', ':Quit:', '....']
uid_who_quit = str(serverMsg[1]).lstrip(':')
self.__Irc.Channel.delete_user_from_all_channel(uid_who_quit)
self.__Irc.User.delete(uid_who_quit)
self.__Irc.Reputation.delete(uid_who_quit)
self.__Irc.Clone.delete(uid_who_quit)
return None
except IndexError as ie:
self.__Base.logs.error(f"{__name__} - Index Error: {ie}")
except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}")
def on_nick(self, serverMsg: list[str]) -> None:
"""Handle nick coming from a server
new nickname
Args:
serverMsg (list[str]): Original server message
"""
try:
# ['@unrealircd.org/geoip=FR;unrealircd.org/', ':001OOU2H3', 'NICK', 'WebIrc', '1703795844']
# Changement de nickname
uid = str(serverMsg[1]).lstrip(':')
newnickname = serverMsg[3]
self.__Irc.User.update(uid, newnickname)
return None
except IndexError as ie:
self.__Base.logs.error(f"{__name__} - Index Error: {ie}")
except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}")
def on_sjoin(self, serverMsg: list[str]) -> None:
"""Handle sjoin coming from a server
Args:
serverMsg (list[str]): Original server message
"""
try:
# ['@msgid=5sTwGdj349D82L96p749SY;time=2024-08-15T09:50:23.528Z', ':001', 'SJOIN', '1721564574', '#welcome', ':001JD94QH']
# ['@msgid=bvceb6HthbLJapgGLXn1b0;time=2024-08-15T09:50:11.464Z', ':001', 'SJOIN', '1721564574', '#welcome', '+lnrt', '13', ':001CIVLQF', '+11ZAAAAAB', '001QGR10C', '*@0014UE10B', '001NL1O07', '001SWZR05', '001HB8G04', '@00BAAAAAJ', '0019M7101']
# ['@msgid=SKUeuVzOrTShRDduq8VerX;time=2024-08-23T19:37:04.266Z', ':001', 'SJOIN', '1723993047', '#welcome', '+lnrt', '13',
# ':001T6VU3F', '001JGWB2K', '@11ZAAAAAB',
# '001F16WGR', '001X9YMGQ', '*+001DYPFGP', '@00BAAAAAJ', '001AAGOG9', '001FMFVG8', '001DAEEG7',
# '&~G:unknown-users', '"~G:websocket-users', '"~G:known-users', '"~G:webirc-users']
serverMsg.pop(0)
channel = str(serverMsg[3]).lower()
len_cmd = len(serverMsg)
list_users:list = []
occurence = 0
start_boucle = 0
# Trouver le premier user
for i in range(len_cmd):
s: list = findall(fr':', serverMsg[i])
if s:
occurence += 1
if occurence == 2:
start_boucle = i
# Boucle qui va ajouter l'ensemble des users (UID)
for i in range(start_boucle, len(serverMsg)):
parsed_UID = str(serverMsg[i])
clean_uid = self.__Irc.User.clean_uid(parsed_UID)
if not clean_uid is None and len(clean_uid) == 9:
list_users.append(parsed_UID)
if list_users:
self.__Irc.Channel.insert(
self.__Irc.Loader.Definition.MChannel(
name=channel,
uids=list_users
)
)
return None
except IndexError as ie:
self.__Base.logs.error(f"{__name__} - Index Error: {ie}")
except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}")
def on_part(self, serverMsg: list[str]) -> None:
"""Handle part coming from a server
Args:
serverMsg (list[str]): Original server message
"""
try:
# ['@unrealircd.org/geoip=FR;unrealircd.org/userhost=50d6492c@80.214.73.44;unrealircd.org/userip=50d6492c@80.214.73.44;msgid=YSIPB9q4PcRu0EVfC9ci7y-/mZT0+Gj5FLiDSZshH5NCw;time=2024-08-15T15:35:53.772Z',
# ':001EPFBRD', 'PART', '#welcome', ':WEB', 'IRC', 'Paris']
uid = str(serverMsg[1]).lstrip(':')
channel = str(serverMsg[3]).lower()
self.__Irc.Channel.delete_user_from_channel(channel, uid)
return None
except IndexError as ie:
self.__Base.logs.error(f"{__name__} - Index Error: {ie}")
except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}")
def on_uid(self, serverMsg: list[str]) -> None:
"""Handle uid message coming from the server
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])
if not 'S' in umodes:
remote_ip = self.__Base.decode_ip(str(serverMsg[13]))
else:
remote_ip = '127.0.0.1'
# extract realname
realname = ' '.join(serverMsg[14:]).lstrip(':')
# Extract Geoip information
pattern = r'^.*geoip=cc=(\S{2}).*$'
geoip_match = match(pattern, serverMsg[0])
if geoip_match:
geoip = geoip_match.group(1)
else:
geoip = None
score_connexion = 0
self.__Irc.User.insert(
self.__Irc.Loader.Definition.MUser(
uid=uid,
nickname=nickname,
username=username,
realname=realname,
hostname=hostname,
umodes=umodes,
vhost=vhost,
isWebirc=isWebirc,
isWebsocket=isWebsocket,
remote_ip=remote_ip,
geoip=geoip,
score_connexion=score_connexion,
connexion_datetime=datetime.now()
)
)
return None
except IndexError as ie:
self.__Base.logs.error(f"{__name__} - Index Error: {ie}")
except Exception as err:
self.__Base.logs.error(f"{__name__} - General Error: {err}")
def on_server_ping(self, serverMsg: list[str]) -> None:
"""Send a PONG message to the server

View File

@@ -52,20 +52,15 @@ class Reputation:
Returns:
bool: True if updated
"""
result = False
for record in self.UID_REPUTATION_DB:
if record.uid == uid:
# If the user exist then update and return True and do not go further
record.nickname = newNickname
result = True
self.Logs.debug(f'Reputation UID ({record.uid}) has been updated with new nickname {newNickname}')
return result
reputationObj = self.get_Reputation(uid)
if not result:
self.Logs.critical(f'Reputation new nickname {newNickname} was not updated, uid = {uid}')
if reputationObj is None:
return False
return result
reputationObj.nickname = newNickname
return True
def delete(self, uid: str) -> bool:
"""Delete the User starting from the UID
@@ -78,6 +73,9 @@ class Reputation:
"""
result = False
if not self.is_exist(uid):
return result
for record in self.UID_REPUTATION_DB:
if record.uid == uid:
# If the user exist then remove and return True and do not go further
@@ -107,9 +105,6 @@ class Reputation:
elif record.nickname == uidornickname:
User = record
if not User is None:
self.Logs.debug(f'Reputation found for {uidornickname} -> {User}')
return User
def get_uid(self, uidornickname:str) -> Union[str, None]:
@@ -121,17 +116,13 @@ class Reputation:
Returns:
str|None: Return the UID
"""
uid = None
for record in self.UID_REPUTATION_DB:
if record.uid == uidornickname:
uid = record.uid
if record.nickname == uidornickname:
uid = record.uid
if not uid is None:
self.Logs.debug(f'Reputation UID found for {uidornickname} -> {uid}')
reputationObj = self.get_Reputation(uidornickname)
return uid
if reputationObj is None:
return None
return reputationObj.uid
def get_nickname(self, uidornickname:str) -> Union[str, None]:
"""Get the Nickname starting from UID or the nickname
@@ -142,17 +133,12 @@ class Reputation:
Returns:
str|None: the nickname
"""
nickname = None
for record in self.UID_REPUTATION_DB:
if record.nickname == uidornickname:
nickname = record.nickname
if record.uid == uidornickname:
nickname = record.nickname
reputationObj = self.get_Reputation(uidornickname)
if not nickname is None:
self.Logs.debug(f'Reputation nickname found for {uidornickname} -> {nickname}')
if reputationObj is None:
return None
return nickname
return reputationObj.nickname
def is_exist(self, uidornickname: str) -> bool:
"""Check if the UID or the nickname exist in the reputation DB
@@ -164,12 +150,9 @@ class Reputation:
bool: True if exist
"""
found = False
reputationObj = self.get_Reputation(uidornickname)
for record in self.UID_REPUTATION_DB:
if record.uid == uidornickname:
found = True
if record.nickname == uidornickname:
found = True
return found
if reputationObj is None:
return False
else:
return True

View File

@@ -8,3 +8,5 @@ class Settings:
RUNNING_SOCKETS: list[socket] = []
PERIODIC_FUNC: dict[object] = {}
LOCK: RLock = RLock()
CONSOLE: bool = True

View File

@@ -1,4 +1,4 @@
import re
from re import sub
from typing import Union, TYPE_CHECKING
from dataclasses import asdict
@@ -26,25 +26,16 @@ class User:
Returns:
bool: True if inserted
"""
result = False
exist = False
for record in self.UID_DB:
if record.uid == newUser.uid:
# If the user exist then return False and do not go further
exist = True
self.Logs.debug(f'{record.uid} already exist')
return result
userObj = self.get_User(newUser.uid)
if not userObj is None:
# User already created return False
return False
if not exist:
self.UID_DB.append(newUser)
result = True
# self.Logs.debug(f'New User Created: ({newUser})')
if not result:
self.Logs.critical(f'The User Object was not inserted {newUser}')
return result
return True
def update(self, uid: str, newNickname: str) -> bool:
"""Update the nickname starting from the UID
@@ -56,20 +47,53 @@ class User:
Returns:
bool: True if updated
"""
result = False
userObj = self.get_User(uidornickname=uid)
for record in self.UID_DB:
if record.uid == uid:
# If the user exist then update and return True and do not go further
record.nickname = newNickname
result = True
# self.Logs.debug(f'UID ({record.uid}) has been updated with new nickname {newNickname}')
return result
if userObj is None:
return False
if not result:
self.Logs.critical(f'The new nickname {newNickname} was not updated, uid = {uid}')
userObj.nickname = newNickname
return result
return True
def update_mode(self, uidornickname: str, modes: str) -> bool:
"""Updating user mode
Args:
uidornickname (str): The UID or Nickname of the user
modes (str): new modes to update
Returns:
bool: True if user mode has been updaed
"""
response = True
userObj = self.get_User(uidornickname=uidornickname)
if userObj is None:
return False
action = modes[0]
new_modes = modes[1:]
existing_umodes = userObj.umodes
final_umodes = userObj.umodes
if action == '+':
for nm in new_modes:
if nm not in existing_umodes:
final_umodes += nm
elif action == '-':
for nm in new_modes:
if nm in existing_umodes:
final_umodes = final_umodes.replace(nm, '')
else:
return False
userObj.umodes = final_umodes
return response
def delete(self, uid: str) -> bool:
"""Delete the User starting from the UID
@@ -80,20 +104,15 @@ class User:
Returns:
bool: True if deleted
"""
result = False
for record in self.UID_DB:
if record.uid == uid:
# If the user exist then remove and return True and do not go further
self.UID_DB.remove(record)
result = True
# self.Logs.debug(f'UID ({record.uid}) has been deleted')
return result
userObj = self.get_User(uidornickname=uid)
if not result:
self.Logs.critical(f'The UID {uid} was not deleted')
if userObj is None:
return False
return result
self.UID_DB.remove(userObj)
return True
def get_User(self, uidornickname: str) -> Union['MUser', None]:
"""Get The User Object model
@@ -111,8 +130,6 @@ class User:
elif record.nickname == uidornickname:
User = record
# self.Logs.debug(f'Search {uidornickname} -- result = {User}')
return User
def get_uid(self, uidornickname:str) -> Union[str, None]:
@@ -124,17 +141,13 @@ class User:
Returns:
str|None: Return the UID
"""
uid = None
for record in self.UID_DB:
if record.uid == uidornickname:
uid = record.uid
if record.nickname == uidornickname:
uid = record.uid
# if not uid is None:
# self.Logs.debug(f'The UID that you are looking for {uidornickname} has been found {uid}')
userObj = self.get_User(uidornickname=uidornickname)
return uid
if userObj is None:
return None
return userObj.uid
def get_nickname(self, uidornickname:str) -> Union[str, None]:
"""Get the Nickname starting from UID or the nickname
@@ -145,26 +158,46 @@ class User:
Returns:
str|None: the nickname
"""
nickname = None
for record in self.UID_DB:
if record.nickname == uidornickname:
nickname = record.nickname
if record.uid == uidornickname:
nickname = record.nickname
# self.Logs.debug(f'The value to check {uidornickname} -> {nickname}')
return nickname
def get_User_AsDict(self, uidornickname: str) -> Union[dict[str, any], None]:
userObj = self.get_User(uidornickname=uidornickname)
if not userObj is None:
user_as_dict = asdict(userObj)
return user_as_dict
else:
if userObj is None:
return None
def clean_uid(self, uid: str) -> str:
return userObj.nickname
def get_User_AsDict(self, uidornickname: str) -> Union[dict[str, any], None]:
"""Transform User Object to a dictionary
Args:
uidornickname (str): The UID or The nickname
Returns:
Union[dict[str, any], None]: User Object as a dictionary or None
"""
userObj = self.get_User(uidornickname=uidornickname)
if userObj is None:
return None
return asdict(userObj)
def is_exist(self, uidornikname: str) -> bool:
"""Check if the UID or the nickname exist in the USER DB
Args:
uidornickname (str): The UID or the NICKNAME
Returns:
bool: True if exist
"""
userObj = self.get_User(uidornickname=uidornikname)
if userObj is None:
return False
return True
def clean_uid(self, uid: str) -> Union[str, None]:
"""Clean UID by removing @ / % / + / ~ / * / :
Args:
@@ -175,6 +208,9 @@ class User:
"""
pattern = fr'[:|@|%|\+|~|\*]*'
parsed_UID = re.sub(pattern, '', uid)
parsed_UID = sub(pattern, '', uid)
if not parsed_UID:
return None
return parsed_UID

View File

@@ -287,5 +287,6 @@ class MClone:
channels: list = field(default_factory=list)
vhost: str = None
hostname: str = 'localhost'
umodes: str = None
remote_ip: str = '127.0.0.1'
group: str = 'Default'

View File

@@ -194,7 +194,7 @@ class Irc:
# Reload configuration
self.Base.logs.debug('Reloading configuration')
self.Config = self.Loader.ConfModule.Configuration().ConfigObject
self.Base = self.Loader.BaseModule.Base(self.Config)
self.Base = self.Loader.BaseModule.Base(self.Config, self.Settings)
self.Protocol = Protocol(self.Config.SERVEUR_PROTOCOL, ircInstance).Protocol
self.init_service_user()
@@ -835,6 +835,10 @@ class Irc:
# self.Base.create_thread(self.abuseipdb_scan, (cmd[7], ))
pass
case 'UMODE2':
# [':adator_', 'UMODE2', '-i']
self.Protocol.on_umode2(serverMsg=original_response)
case 'SQUIT':
# ['@msgid=QOEolbRxdhpVW5c8qLkbAU;time=2024-09-21T17:33:16.547Z', 'SQUIT', 'defender.deb.biz.st', ':Connection', 'closed']
server_hostname = interm_response[1]
@@ -846,19 +850,6 @@ class Irc:
self.User.delete(uid_to_delete)
self.Channel.delete_user_from_all_channel(uid_to_delete)
case 'SJOIN':
# If Server Join channels
# [':11Z', 'SJOIN', '1726940687', '#welcome', '+', ':11ZAAAAAB']
channel_joined = original_response[3]
server_uid = self.Base.clean_uid(original_response[5])
self.Channel.insert(
self.Loader.Definition.MChannel(
name=channel_joined,
uids=[server_uid]
)
)
case 'REPUTATION':
# :001 REPUTATION 127.0.0.1 118
try:
@@ -963,135 +954,34 @@ class Irc:
self.Protocol.on_version_msg(original_response)
case 'QUIT':
# :001N1WD7L QUIT :Quit: free_znc_1
uid_who_quit = str(interm_response[0]).replace(':', '')
self.User.delete(uid_who_quit)
self.Channel.delete_user_from_all_channel(uid_who_quit)
self.Protocol.on_quit(serverMsg=original_response)
case 'PONG':
# ['@msgid=aTNJhp17kcPboF5diQqkUL;time=2023-12-28T20:35:58.411Z', ':irc.deb.biz.st', 'PONG', 'irc.deb.biz.st', ':Dev-PyDefender']
self.Base.execute_periodic_action()
case 'NICK':
# ['@unrealircd.org/geoip=FR;unrealircd.org/', ':001OOU2H3', 'NICK', 'WebIrc', '1703795844']
# Changement de nickname
uid = str(interm_response[0]).replace(':','')
newnickname = interm_response[2]
self.User.update(uid, newnickname)
self.Protocol.on_nick(original_response)
case 'MODE':
#['@msgid=d0ySx56Yd0nc35oHts2SkC-/J9mVUA1hfM6+Z4494xWUg;time=2024-08-09T12:45:36.651Z',
# ':001', 'MODE', '#a', '+nt', '1723207536']
# [':adator_', 'UMODE2', '-i']
pass
case 'SJOIN':
# ['@msgid=5sTwGdj349D82L96p749SY;time=2024-08-15T09:50:23.528Z', ':001', 'SJOIN', '1721564574', '#welcome', ':001JD94QH']
# ['@msgid=bvceb6HthbLJapgGLXn1b0;time=2024-08-15T09:50:11.464Z', ':001', 'SJOIN', '1721564574', '#welcome', '+lnrt', '13', ':001CIVLQF', '+11ZAAAAAB', '001QGR10C', '*@0014UE10B', '001NL1O07', '001SWZR05', '001HB8G04', '@00BAAAAAJ', '0019M7101']
# ['@msgid=SKUeuVzOrTShRDduq8VerX;time=2024-08-23T19:37:04.266Z', ':001', 'SJOIN', '1723993047', '#welcome', '+lnrt', '13',
# ':001T6VU3F', '001JGWB2K', '@11ZAAAAAB',
# '001F16WGR', '001X9YMGQ', '*+001DYPFGP', '@00BAAAAAJ', '001AAGOG9', '001FMFVG8', '001DAEEG7',
# '&~G:unknown-users', '"~G:websocket-users', '"~G:known-users', '"~G:webirc-users']
channel = str(interm_response[3]).lower()
len_cmd = len(interm_response)
list_users:list = []
occurence = 0
start_boucle = 0
# Trouver le premier user
for i in range(len_cmd):
s: list = re.findall(fr':', interm_response[i])
if s:
occurence += 1
if occurence == 2:
start_boucle = i
# Boucle qui va ajouter l'ensemble des users (UID)
for i in range(start_boucle, len(interm_response)):
parsed_UID = str(interm_response[i])
# pattern = fr'[:|@|%|\+|~|\*]*'
# pattern = fr':'
# parsed_UID = re.sub(pattern, '', parsed_UID)
clean_uid = self.Base.clean_uid(parsed_UID)
if len(clean_uid) == 9:
list_users.append(parsed_UID)
self.Channel.insert(
self.Loader.Definition.MChannel(
name=channel,
uids=list_users
)
)
self.Protocol.on_sjoin(serverMsg=original_response)
case 'PART':
# ['@unrealircd.org/geoip=FR;unrealircd.org/userhost=50d6492c@80.214.73.44;unrealircd.org/userip=50d6492c@80.214.73.44;msgid=YSIPB9q4PcRu0EVfC9ci7y-/mZT0+Gj5FLiDSZshH5NCw;time=2024-08-15T15:35:53.772Z',
# ':001EPFBRD', 'PART', '#welcome', ':WEB', 'IRC', 'Paris']
try:
uid = str(interm_response[0]).replace(':','')
channel = str(interm_response[2]).lower()
self.Channel.delete_user_from_channel(channel, uid)
except IndexError as ie:
self.Base.logs.error(f'Index Error: {ie}')
self.Protocol.on_part(serverMsg=original_response)
case 'UID':
try:
# ['@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==', ':...']
isWebirc = True if 'webirc' in original_response[0] else False
isWebsocket = True if 'websocket' in original_response[0] else False
uid = str(original_response[8])
nickname = str(original_response[3])
username = str(original_response[6])
hostname = str(original_response[7])
umodes = str(original_response[10])
vhost = str(original_response[11])
if not 'S' in umodes:
remote_ip = self.Base.decode_ip(str(original_response[13]))
else:
remote_ip = '127.0.0.1'
# extract realname
realname_list = []
for i in range(14, len(original_response)):
realname_list.append(original_response[i])
realname = ' '.join(realname_list)[1:]
# Extract Geoip information
pattern = r'^.*geoip=cc=(\S{2}).*$'
geoip_match = re.match(pattern, original_response[0])
if geoip_match:
geoip = geoip_match.group(1)
else:
geoip = None
score_connexion = self.first_score
self.User.insert(
self.Loader.Definition.MUser(
uid=uid,
nickname=nickname,
username=username,
realname=realname,
hostname=hostname,
umodes=umodes,
vhost=vhost,
isWebirc=isWebirc,
isWebsocket=isWebsocket,
remote_ip=remote_ip,
geoip=geoip,
score_connexion=score_connexion,
connexion_datetime=datetime.now()
)
)
self.Protocol.on_uid(serverMsg=original_response)
for classe_name, classe_object in self.loaded_classes.items():
classe_object.cmd(original_response)
@@ -1596,18 +1486,18 @@ class Irc:
reason.append(cmd[i])
final_reason = ' '.join(reason)
self.User.UID_DB.clear() # Clear User Object
self.Channel.UID_CHANNEL_DB.clear() # Clear Channel Object
for class_name in self.loaded_classes:
self.loaded_classes[class_name].unload()
self.Protocol.sendNotice(
nick_from=dnickname,
nick_to=fromuser,
msg=f"Redémarrage du service {dnickname}"
)
for class_name in self.loaded_classes:
self.loaded_classes[class_name].unload()
self.User.UID_DB.clear() # Clear User Object
self.Channel.UID_CHANNEL_DB.clear() # Clear Channel Object
self.Protocol.squit(server_id=self.Config.SERVEUR_ID, server_link=self.Config.SERVEUR_LINK, reason=final_reason)
self.Base.logs.info(f'Redémarrage du server {dnickname}')
self.loaded_classes.clear()

View File

@@ -63,7 +63,6 @@ class Clone():
# Créer les tables necessaire a votre module (ce n'es pas obligatoire)
self.__create_tables()
self.CloneCopy = [self.Definition.MClone()]
self.stop = False
logging.getLogger('faker').setLevel(logging.CRITICAL)
@@ -74,7 +73,7 @@ class Clone():
self.__load_module_configuration()
self.Channel.db_query_channel(action='add', module_name=self.module_name, channel_name=self.Config.CLONE_CHANNEL)
self.Protocol.join(self.Config.SERVICE_NICKNAME, self.Config.CLONE_CHANNEL)
self.Protocol.sendChanJoin(self.Config.SERVICE_NICKNAME, self.Config.CLONE_CHANNEL)
self.Protocol.send2socket(f":{self.Config.SERVICE_NICKNAME} SAMODE {self.Config.CLONE_CHANNEL} +o {self.Config.SERVICE_NICKNAME}")
self.Protocol.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.CLONE_CHANNEL} +nts")
@@ -141,7 +140,7 @@ class Clone():
self.Channel.db_query_channel(action='del', module_name=self.module_name, channel_name=self.Config.CLONE_CHANNEL)
self.Protocol.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.CLONE_CHANNEL} -nts")
self.Protocol.send2socket(f":{self.Config.SERVICE_NICKNAME} MODE {self.Config.CLONE_CHANNEL} -k {self.Config.CLONE_CHANNEL_PASSWORD}")
self.Protocol.part(self.Config.SERVICE_NICKNAME, self.Config.CLONE_CHANNEL)
self.Protocol.sendChanPart(self.Config.SERVICE_NICKNAME, self.Config.CLONE_CHANNEL)
return None
@@ -168,6 +167,8 @@ class Clone():
generate_uid = fakeEN.random_sample(chaine, 6)
uid = self.Config.SERVEUR_ID + ''.join(generate_uid)
umodes = '+iwxz'
# Generate Username
chaine = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
new_username = fakeEN.random_sample(chaine, 9)
@@ -188,7 +189,8 @@ class Clone():
department = fakeFR.department_name()
realname = f'{age} {gender} {department}'
ip = self.Base.encode_ip(fakeEN.ipv4_private())
decoded_ip = fakeEN.ipv4_private()
hostname = fakeEN.hostname()
vhost = self.generate_vhost()
@@ -212,15 +214,16 @@ class Clone():
nickname=nickname,
username=username,
realname=realname,
hostname=hostname,
umodes=umodes,
uid=uid,
remote_ip=ip,
remote_ip=decoded_ip,
vhost=vhost,
group=group,
channels=[]
)
self.Clone.insert(clone)
self.CloneCopy.append(clone)
return None
@@ -242,18 +245,8 @@ class Clone():
break
if not clone.connected:
cloneObj_asdict = self.Clone.get_Clone_AsDict(clone.uid)
for key in ['connected','group','channels']:
cloneObj_asdict.pop(key, None)
self.User.insert(
self.Definition.MUser(**cloneObj_asdict)
)
self.Protocol.send2socket(f":{self.Config.SERVEUR_ID} UID {clone.nickname} 1 {self.Base.get_unixtime()} {clone.username} {clone.hostname} {clone.uid} * +ixwz {clone.vhost} * {clone.remote_ip} :{clone.realname}", False)
self.Protocol.join(uidornickname=clone.uid, channel=self.Config.CLONE_CHANNEL, password=self.Config.CLONE_CHANNEL_PASSWORD, print_log=False)
self.Irc.Channel.insert(self.Irc.Loader.Definition.MChannel(name=self.Config.CLONE_CHANNEL, uids=[clone.uid]))
self.Protocol.sendUID(clone.nickname, clone.username, clone.hostname, clone.uid, clone.umodes, clone.vhost, clone.remote_ip, clone.realname, print_log=False)
self.Protocol.sendChanJoin(uidornickname=clone.uid, channel=self.Config.CLONE_CHANNEL, password=self.Config.CLONE_CHANNEL_PASSWORD, print_log=False)
time.sleep(interval)
clone.connected = True
@@ -262,47 +255,13 @@ class Clone():
clone_to_kill: list[str] = []
for clone in self.Clone.UID_CLONE_DB:
self.Protocol.send2socket(f':{clone.uid} QUIT :Goood bye', False)
clone_to_kill.append(clone.uid)
for clone_uid in clone_to_kill:
self.Irc.Channel.delete_user_from_all_channel(clone_uid)
self.Clone.delete(clone_uid)
self.User.delete(clone_uid)
self.Protocol.sendQuit(clone_uid, 'Gooood bye', print_log=False)
del clone_to_kill
self.clean_clones(fromuser)
return None
def clean_clones(self, fromuser: str) -> None:
connected = 0
for c in self.CloneCopy:
if c.connected:
connected += 1
self.Protocol.sendNotice(nick_from=self.Config.SERVICE_NICKNAME, nick_to=fromuser, msg=f"Clean in progress | Total Clones in memory {len(self.CloneCopy) - 1} - {connected} / {len(self.CloneCopy) - 1} Connected ...")
clone_to_kill: list[str] = []
# clean from Channels
for clone in self.CloneCopy:
self.Irc.Channel.delete_user_from_all_channel(clone.uid)
# clean from users
for clone in self.CloneCopy:
self.Protocol.send2socket(f':{clone.uid} QUIT :Goood bye', False)
clone_to_kill.append(clone.uid)
# clean original clone object
for clone_uid in clone_to_kill:
self.User.delete(clone_uid)
self.Clone.delete(clone_uid)
self.CloneCopy = [self.Definition.MClone()]
self.Protocol.sendNotice(nick_from=self.Config.SERVICE_NICKNAME, nick_to=fromuser,
msg="Clone cleaning done!")
return None
def cmd(self, data:list) -> None:
@@ -353,6 +312,7 @@ class Clone():
try:
command = str(cmd[0]).lower()
fromuser = user
print(command)
dnickname = self.Config.SERVICE_NICKNAME # Defender nickname
@@ -400,15 +360,9 @@ class Clone():
self.Base.create_thread(func=self.thread_kill_clones, func_args=(fromuser, ))
else:
if self.Clone.exists(clone_name):
self.Protocol.send2socket(f':{clone_name} QUIT :Goood bye')
clone_uid = self.Clone.get_uid(clone_name)
if not clone_uid is None:
self.Irc.Channel.delete_user_from_all_channel(clone_uid)
self.Clone.delete(clone_name)
self.User.delete(clone_uid)
cloneObj = self.Clone.get_Clone(clone_name)
if not cloneObj is None:
self.Protocol.sendQuit(cloneObj.uid, 'Goood bye', print_log=False)
except Exception as err:
self.Logs.error(f'{err}')
@@ -424,14 +378,12 @@ class Clone():
if clone_name.lower() == 'all':
for clone in self.Clone.UID_CLONE_DB:
self.Protocol.join(uidornickname=clone.uid, channel=clone_channel_to_join, print_log=False)
self.Irc.Channel.insert(self.Irc.Loader.Definition.MChannel(name=clone_channel_to_join, uids=[clone.uid]))
self.Protocol.sendChanJoin(uidornickname=clone.uid, channel=clone_channel_to_join, print_log=False)
else:
if self.Clone.exists(clone_name):
if not self.Clone.get_uid(clone_name) is None:
self.Protocol.join(uidornickname=clone_name, channel=clone_channel_to_join, print_log=False)
self.Irc.Channel.insert(self.Irc.Loader.Definition.MChannel(name=clone_channel_to_join, uids=[self.Clone.get_uid(clone_name)]))
self.Protocol.sendChanJoin(uidornickname=clone_name, channel=clone_channel_to_join, print_log=False)
except Exception as err:
self.Logs.error(f'{err}')
@@ -447,15 +399,13 @@ class Clone():
if clone_name.lower() == 'all':
for clone in self.Clone.UID_CLONE_DB:
self.Protocol.part(uidornickname=clone.uid, channel=clone_channel_to_part, print_log=False)
self.Irc.Channel.delete_user_from_channel(clone_channel_to_part, clone.uid)
self.Protocol.sendChanPart(uidornickname=clone.uid, channel=clone_channel_to_part, print_log=False)
else:
if self.Clone.exists(clone_name):
clone_uid = self.Clone.get_uid(clone_name)
if not clone_uid is None:
self.Protocol.part(uidornickname=clone_uid, channel=clone_channel_to_part, print_log=False)
self.Irc.Channel.delete_user_from_channel(clone_channel_to_part, clone_uid)
self.Protocol.sendChanPart(uidornickname=clone_uid, channel=clone_channel_to_part, print_log=False)
except Exception as err:
self.Logs.error(f'{err}')

View File

@@ -226,7 +226,7 @@ class Command():
return None
def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None:
def _hcmds(self, user: str, channel: any, cmd: list, fullcmd: list = []) -> None:
command = str(cmd[0]).lower()
dnickname = self.Config.SERVICE_NICKNAME
@@ -656,7 +656,7 @@ class Command():
return False
# self.Protocol.send2socket(f':{service_id} JOIN {sent_channel}')
self.Protocol.join(uidornickname=dnickname,channel=sent_channel)
self.Protocol.sendChanJoin(uidornickname=dnickname,channel=sent_channel)
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f" {dnickname} JOINED {sent_channel}")
self.Channel.db_query_channel('add', self.module_name, sent_channel)
@@ -677,7 +677,7 @@ class Command():
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f" {dnickname} CAN'T LEFT {sent_channel} AS IT IS LOG CHANNEL")
return False
self.Protocol.part(uidornickname=dnickname, channel=sent_channel)
self.Protocol.sendChanPart(uidornickname=dnickname, channel=sent_channel)
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f" {dnickname} LEFT {sent_channel}")
self.Channel.db_query_channel('del', self.module_name, sent_channel)
@@ -859,6 +859,10 @@ class Command():
case 'umode':
try:
# .umode nickname +mode
if len(cmd) < 2:
self.Protocol.sendNotice(nick_from=dnickname, nick_to=fromuser, msg=f" Right command : /msg {dnickname} {command.upper()} [NICKNAME] [+/-]mode")
return None
nickname = str(cmd[1])
umode = str(cmd[2])

View File

@@ -1364,7 +1364,7 @@ class Defender():
self.Protocol.sendPrivMsg(nick_from=dnickname, msg=f"[ {self.Config.COLORS.green}REPUTATION{self.Config.COLORS.black} ] : Activated by {fromuser}", channel=dchanlog)
self.Protocol.join(uidornickname=dnickname, channel=jail_chan)
self.Protocol.sendChanJoin(uidornickname=dnickname, channel=jail_chan)
self.Protocol.send2socket(f":{service_id} SAMODE {jail_chan} +{dumodes} {dnickname}")
self.Protocol.send2socket(f":{service_id} MODE {jail_chan} +{jail_chan_mode}")
@@ -1786,7 +1786,7 @@ class Defender():
if activation == 'on':
for chan in self.Channel.UID_CHANNEL_DB:
if not chan.name in channel_to_dont_quit:
self.Protocol.join(uidornickname=dnickname, channel=chan.name)
self.Protocol.sendChanJoin(uidornickname=dnickname, channel=chan.name)
if activation == 'off':
for chan in self.Channel.UID_CHANNEL_DB:
if not chan.name in channel_to_dont_quit: