patch 1e0f59717da4cd1b659c91ef4b75715620d33325 Author: E. Bosch Date: Sun Jan 24 03:31:52 CET 2021 * irc: Improve parsing of IRC commands Add IRC replies/errors numerics diff -rN -u old-irgramd/irc.py new-irgramd/irc.py --- old-irgramd/irc.py 2024-10-23 08:26:26.700033193 +0200 +++ new-irgramd/irc.py 2024-10-23 08:26:26.700033193 +0200 @@ -10,16 +10,17 @@ # Local modules from utils import chunks +from irc_replies import irc_codes # IRC Regular Expressions -PREFIX = r'(:[^ ]+ +|)' -IRC_NICK_RX = re.compile(PREFIX + r'NICK +(:|)(?P[^\n\r]+)') -IRC_PASS_RX = re.compile(PREFIX + r'PASS +(:|)(?P[^\n\r]+)') -IRC_PING_RX = re.compile(PREFIX + r'PING +(:|)(?P[^\n\r]+)') -IRC_PRIVMSG_RX = re.compile(PREFIX + r'PRIVMSG +(?P[^ ]+) +(:|):(?P[^\n\r]+)') -IRC_USER_RX = re.compile(PREFIX + r'USER +(?P[^ ]+) +[^ ]+ +[^ ]+ +(:|)(?P[^\n\r]+)') -IRC_JOIN_RX = re.compile(PREFIX + r'JOIN +(?P[^ ]+)') +PREFIX = r'(:[^ ]+ +|)' +IRC_NICK_RX = re.compile(PREFIX + r'NICK( +:| +|\n)(?P[^\n]+|)') +IRC_PASS_RX = re.compile(PREFIX + r'PASS( +:| +|\n)(?P[^\n]+|)') +IRC_PING_RX = re.compile(PREFIX + r'PING( +:| +|\n)(?P[^\n]+|)') +IRC_PRIVMSG_RX = re.compile(PREFIX + r'PRIVMSG( +|\n)(?P[^ ]+)( +:| +|\n)(?P[^\n]+|)') +IRC_USER_RX = re.compile(PREFIX + r'USER( +|\n)(?P[^ ]+) +[^ ]+ +[^ ]+( +:| +|\n)(?P[^\n]+|)') +IRC_JOIN_RX = re.compile(PREFIX + r'JOIN( +|\n)(?P[^ ]+)') # IRC Handler @@ -43,13 +44,25 @@ while True: message = await user.stream.read_until(b'\n') - message = message.decode().rstrip() + message = message.decode() self.logger.debug(message) + matched = False - for pattern, handler in self.irc_handlers: + for pattern, handler, register_required in self.irc_handlers: matches = pattern.match(message) if matches: - await handler(user, **matches.groupdict()) + matched = True + if user.registered or not register_required: + params = matches.groupdict() + num_params = len([x for x in params.values() if x]) + num_params_expected = len(params.keys()) + if num_params >= num_params_expected: + await handler(user, **params) + else: + await self.reply(user, 'ERR_NEEDMOREPARAMS') + + if not matched and user.registered: + await self.reply(user, 'ERR_UNKNOWNCOMMAND') def set_telegram(self, tg): self.tg = tg @@ -58,12 +71,13 @@ def initialize_irc(self): self.irc_handlers = ( - (IRC_NICK_RX , self.handle_irc_nick), - (IRC_PASS_RX , self.handle_irc_pass), - (IRC_PING_RX , self.handle_irc_ping), - (IRC_PRIVMSG_RX, self.handle_irc_privmsg), - (IRC_USER_RX , self.handle_irc_user), - (IRC_JOIN_RX , self.handle_irc_join), + # pattern handle register_required + (IRC_NICK_RX, self.handle_irc_nick, False), + (IRC_PASS_RX, self.handle_irc_pass, False), + (IRC_PING_RX, self.handle_irc_ping, False), + (IRC_PRIVMSG_RX, self.handle_irc_privmsg, True), + (IRC_USER_RX, self.handle_irc_user, False), + (IRC_JOIN_RX, self.handle_irc_join, True), ) self.iid_to_tid = {} self.irc_channels = collections.defaultdict(set) @@ -122,6 +136,14 @@ telegram_id = self.iid_to_tid[nick] await self.tg.telegram_client.send_message(telegram_id, message) + # IRC functions + + async def reply(self, user, code): + num, tail = irc_codes[code] + await self.send_irc_command(user, ':{} {} {} :{}'.format( + self.hostname, num, user.irc_nick, tail + )) + async def join_irc_channel(self, user, channel, full_join=False): self.irc_channels[channel].add(user.irc_nick) @@ -162,3 +184,5 @@ self.irc_nick = None self.irc_username = None self.irc_realname = None + self.registered = True + self.recv_pass = '' diff -rN -u old-irgramd/irc_replies.py new-irgramd/irc_replies.py --- old-irgramd/irc_replies.py 1970-01-01 01:00:00.000000000 +0100 +++ new-irgramd/irc_replies.py 2024-10-23 08:26:26.700033193 +0200 @@ -0,0 +1,56 @@ + +irc_codes = \ +{ + 'RPL_WELCOME': ('001', 'Welcome to the irgramd gateway, {}'), + 'RPL_YOURHOST': ('002', 'Host {} is running irgramd version {}'), + 'RPL_CREATED': ('003', 'This server was created {}'), + 'RPL_MYINFO': ('004', '{} ircgramd {} o nt'), + 'RPL_ISUPPORT': ('005', 'CASEMAPPING=ascii CHANLIMIT=#&+: CHANTYPES=&#+ CHANMODES=,,,nt CHANNELLEN={} NICKLEN={} SAFELIST'), + 'RPL_MOTDSTART': ('375', '- {} Message of the day - '), + 'RPL_MOTD': ('372', '{}'), + 'RPL_ENDOFMOTD': (376, 'End of MOTD command'), + 'ERR_NOSUCHNICK': ('401', 'No such nick'), + 'ERR_NOSUCHSERVER': ('402', 'No such server'), + 'ERR_NOSUCHCHANNEL': ('403', 'No such channel'), + 'ERR_CANNOTSENDTOCHAN': ('404', 'Cannot send to channel'), + 'ERR_TOOMANYCHANNELS': ('405', 'Too many channels'), + 'ERR_WASNOSUCHNICK': ('406', 'There was no such nick'), + 'ERR_TOOMANYTARGETS': ('407', 'Too many targets'), + 'ERR_NOORIGIN': ('409', 'No origin present'), + 'ERR_NORECIPIENT': ('411', 'No recipient'), + 'ERR_NOTEXTTOSEND': ('412', 'No text to send'), + 'ERR_NOTOPLEVEL': ('413', 'No top level domain'), + 'ERR_WILDTOPLEVEL': ('414', 'Wild top level domain'), + 'ERR_UNKNOWNCOMMAND': ('421', 'Unknown command'), + 'ERR_NOMOTD': ('422', 'No MOTD'), + 'ERR_NOADMININFO': ('423', 'No admin info'), + 'ERR_FILEERROR': ('424', 'File error'), + 'ERR_NONICKNAMEGIVEN': ('431', 'No nickname given'), + 'ERR_ERRONEUSNICKNAME': ('432', 'Erroneus nickname'), + 'ERR_NICKNAMEINUSE': ('433', 'Nickname in use'), + 'ERR_NICKCOLLISION': ('436', 'Nickname collision'), + 'ERR_USERNOTINCHANNEL': ('441', 'User not in channel'), + 'ERR_NOTONCHANNEL': ('442', 'Not on channel'), + 'ERR_USERONCHANNEL': ('443', 'User on channel'), + 'ERR_NOLOGIN': ('444', 'No login'), + 'ERR_SUMMONDISABLED': ('445', 'Summon disabled'), + 'ERR_USERSDISABLED': ('446', 'Users disabled'), + 'ERR_NOTREGISTERED': ('451', 'Not registered'), + 'ERR_NEEDMOREPARAMS': ('461', 'Need more params'), + 'ERR_ALREADYREGISTRED': ('462', 'Already registered'), + 'ERR_NOPERMFORHOST': ('463', 'Insufficient permissions for host'), + 'ERR_PASSWDMISMATCH': ('464', 'Password mismatch'), + 'ERR_YOUREBANNEDCREEP': ('465', 'You\'re banned, creep'), + 'ERR_KEYSET': ('467', 'Key set'), + 'ERR_CHANNELISFULL': ('471', 'Channel is full'), + 'ERR_UNKNOWNMODE': ('472', 'Unknown mode'), + 'ERR_INVITEONLYCHAN': ('473', 'Invite only channel'), + 'ERR_BANNEDFROMCHAN': ('474', 'Banned from channel'), + 'ERR_BADCHANNELKEY': ('475', 'Bad channel key'), + 'ERR_NOPRIVILEGES': ('481', 'No privileges'), + 'ERR_CHANOPRIVSNEEDED': ('482', 'Channel +o privileges needed'), + 'ERR_CANTKILLSERVER': ('483', 'Cannot kill server'), + 'ERR_NOOPERHOST': ('491', 'No operator host'), + 'ERR_UMODEUNKNOWNFLAG': ('501', 'User mode unknown flag'), + 'ERR_USERSDONTMATCH': ('502', 'Users don\'t match'), +}