irc: Add registration handling of new connections
patch f6adfec3b3a67fe89d7f7528ea918372f8070ace
Author: E. Bosch <presidev@AT@gmail.com>
Date: Wed Jan 27 20:51:33 CET 2021
* irc: Add registration handling of new connections
Improve replies, error responses, motd, nick validation, etc.
diff -rN -u old-irgramd/include.py new-irgramd/include.py
--- old-irgramd/include.py 1970-01-01 01:00:00.000000000 +0100
+++ new-irgramd/include.py 2024-10-23 02:31:29.863458534 +0200
@@ -0,0 +1,6 @@
+
+# Constants
+
+VERSION = '0.1'
+NICK_MAX_LENGTH = 20
+CHAN_MAX_LENGHT = 50
diff -rN -u old-irgramd/irc.py new-irgramd/irc.py
--- old-irgramd/irc.py 2024-10-23 02:31:29.863458534 +0200
+++ new-irgramd/irc.py 2024-10-23 02:31:29.863458534 +0200
@@ -3,14 +3,22 @@
import logging
import re
import socket
+import string
+import time
import tornado.httpclient
import tornado.ioloop
# Local modules
-from utils import chunks
+from include import VERSION, CHAN_MAX_LENGHT, NICK_MAX_LENGTH
from irc_replies import irc_codes
+from utils import chunks
+
+# Constants
+
+VALID_IRC_NICK_FIRST_CHARS = string.ascii_letters + '[]\`_^{|}'
+VALID_IRC_NICK_CHARS = VALID_IRC_NICK_FIRST_CHARS + string.digits + '-'
# IRC Regular Expressions
@@ -81,6 +89,7 @@
)
self.iid_to_tid = {}
self.irc_channels = collections.defaultdict(set)
+ self.start_time = time.strftime('%a %d %b %Y %H:%M:%S %z')
def get_irc_user_mask(self, nick):
return '{}!{}@{}'.format(nick, nick, self.hostname)
@@ -90,37 +99,51 @@
command = command + '\r\n'
user.stream.write(command.encode())
+ # IRC handlers
+
+ async def handle_irc_pass(self, user, password):
+ self.logger.debug('Handling PASS: %s %s', password)
+
+ if user.registered:
+ await self.reply(user, 'ERR_ALREADYREGISTRED')
+ else:
+ user.recv_pass = password
+
async def handle_irc_nick(self, user, nick):
self.logger.debug('Handling NICK: %s', nick)
- if user.irc_nick in self.iid_to_tid:
- tid = self.iid_to_tid[user.irc_nick]
- self.tg.tid_to_iid[tid] = nick
- self.iid_to_tid[nick] = tid
-
- user.irc_nick = nick
+ if not self.valid_nick(nick):
+ await self.reply(user, 'ERR_ERRONEUSNICKNAME')
+ elif nick in [x.irc_nick for x in self.users if x is not user]:
+ await self.reply(user, 'ERR_NICKNAMEINUSE')
+ elif user.password == user.recv_pass:
+ user.irc_nick = nick
+
+ if user.irc_nick in self.iid_to_tid:
+ tid = self.iid_to_tid[user.irc_nick]
+ self.tg.tid_to_iid[tid] = nick
+ self.iid_to_tid[nick] = tid
+
+ if not user.registered and user.irc_username:
+ user.registered = True
+ await self.send_greeting(user)
+ else:
+ await self.reply(user, 'ERR_PASSWDMISMATCH')
async def handle_irc_user(self, user, username, realname):
self.logger.debug('Handling USER: %s, %s', username, realname)
user.irc_username = username
user.irc_realname = realname
-
- await self.send_irc_command(user, ':{} 001 {} :{}'.format(
- self.hostname, user.irc_nick, 'Welcome to irgramd'
- ))
- await self.send_irc_command(user, ':{} 376 {} :{}'.format(
- self.hostname, user.irc_nick, 'End of MOTD command'
- ))
+ if user.irc_nick:
+ user.registered = True
+ await self.send_greeting(user)
async def handle_irc_join(self, user, channel):
self.logger.debug('Handling JOIN: %s', channel)
await self.join_irc_channel(user, channel, True)
- async def handle_irc_pass(self, user, app_id, app_hash):
- self.logger.debug('Handling PASS: %s %s', app_id, app_hash)
-
async def handle_irc_ping(self, user, payload):
self.logger.debug('Handling PING: %s', payload)
await self.send_irc_command(user, ':{} PONG {} :{}'.format(
@@ -144,6 +167,40 @@
self.hostname, num, user.irc_nick, tail
))
+ async def reply_param(self, user, num, rest):
+ await self.send_irc_command(user, ':{} {} {} {}'.format(
+ self.hostname, num, user.irc_nick, rest
+ ))
+
+ async def send_greeting(self, user):
+ num, rest = irc_codes['RPL_WELCOME']
+ await self.reply_param(user, num, rest.format(user.irc_nick))
+ num, rest = irc_codes['RPL_YOURHOST']
+ await self.reply_param(user, num, rest.format(self.hostname, VERSION))
+ num, rest = irc_codes['RPL_CREATED']
+ await self.reply_param(user, num, rest.format(self.start_time))
+ num, rest = irc_codes['RPL_MYINFO']
+ await self.reply_param(user, num, rest.format(self.hostname, VERSION))
+ num, rest = irc_codes['RPL_ISUPPORT']
+ await self.reply_param(user, num, rest.format(str(CHAN_MAX_LENGHT), str(NICK_MAX_LENGTH)))
+ await self.send_motd(user)
+
+ async def send_motd(self, user):
+ num, rest = irc_codes['RPL_MOTDSTART']
+ await self.reply_param(user, num, rest.format(self.hostname))
+ num, rest = irc_codes['RPL_MOTD']
+ await self.reply_param(user, num, rest.format('Welcome to the irgramd server'))
+ await self.reply_param(user, num, rest.format(''))
+ await self.reply_param(user, num, rest.format('This is not a normal IRC server, it\'s a gateway that'))
+ await self.reply_param(user, num, rest.format('allows connecting from an IRC client (the program that'))
+ await self.reply_param(user, num, rest.format('you are [probably] using right now) to the Telegram instant'))
+ await self.reply_param(user, num, rest.format('messaging network as a regular user account (not bot)'))
+ await self.reply_param(user, num, rest.format(''))
+ await self.reply_param(user, num, rest.format('irgramd is an open source project that you can find on'))
+ await self.reply_param(user, num, rest.format('git repository: https://github.com/prsai/irgramd'))
+ await self.reply_param(user, num, rest.format('darcs repository: https://src.presi.org/darcs/irgramd'))
+ await self.reply(user, 'RPL_ENDOFMOTD')
+
async def join_irc_channel(self, user, channel, full_join=False):
self.irc_channels[channel].add(user.irc_nick)
@@ -177,6 +234,14 @@
self.get_irc_user_mask(user.irc_nick), channel
))
+ def valid_nick(self, nick):
+ if len(nick) <= NICK_MAX_LENGTH and nick[0] in VALID_IRC_NICK_FIRST_CHARS:
+ for x in nick[1:]:
+ if x not in VALID_IRC_NICK_CHARS:
+ return 0
+ return 1
+ else: return 0
+
class IRCUser(object):
def __init__(self, stream, address):
self.stream = stream
@@ -185,4 +250,5 @@
self.irc_username = None
self.irc_realname = None
self.registered = True
+ self.password = ''
self.recv_pass = ''
diff -rN -u old-irgramd/irc_replies.py new-irgramd/irc_replies.py
--- old-irgramd/irc_replies.py 2024-10-23 02:31:29.863458534 +0200
+++ new-irgramd/irc_replies.py 2024-10-23 02:31:29.867458527 +0200
@@ -1,13 +1,13 @@
irc_codes = \
{
- 'RPL_WELCOME': ('001', 'Welcome to the irgramd gateway, {}'),
- 'RPL_YOURHOST': ('002', 'Your host is {}, running version irgramd-{}'),
- 'RPL_CREATED': ('003', 'This server was created {}'),
+ 'RPL_WELCOME': ('001', ':Welcome to the irgramd gateway, {}'),
+ 'RPL_YOURHOST': ('002', ':Your host is {}, running version irgramd-{}'),
+ 'RPL_CREATED': ('003', ':This server was created {}'),
'RPL_MYINFO': ('004', '{} irgramd-{} 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_ISUPPORT': ('005', 'CASEMAPPING=ascii CHANLIMIT=#&+: CHANTYPES=&#+ CHANMODES=,,,nt CHANNELLEN={} NICKLEN={} SAFELIST :are supported by this server'),
+ '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'),
diff -rN -u old-irgramd/telegram.py new-irgramd/telegram.py
--- old-irgramd/telegram.py 2024-10-23 02:31:29.863458534 +0200
+++ new-irgramd/telegram.py 2024-10-23 02:31:29.867458527 +0200
@@ -3,6 +3,10 @@
import os
import telethon
+# Local modules
+
+from include import CHAN_MAX_LENGHT, NICK_MAX_LENGTH
+
# Configuration
# GET API_ID and API_HASH from https://my.telegram.org/apps
@@ -11,7 +15,6 @@
TELEGRAM_API_ID =
TELEGRAM_API_HASH = ''
-NICK_MAX_LENGTH = 20
# Telegram