from ircplom.events import (AffectiveEvent, ExceptionEvent, Logger,
PayloadMixin, QueueMixin)
from ircplom.irc_conn import (BaseIrcConnection, IrcConnAbortException,
- IrcMessage)
+ IrcMessage, PORT_SSL)
ClientsDb = dict[UUID, 'Client']
CHAT_GLOB = '*'
class IrcConnSetup:
'All we need to know to set up a new Client connection.'
hostname: str
+ port: int
nickname: str
realname: str
password: str
super().__init__(**kwargs)
self._caps = _CapsManager(self.send)
self.conn_setup = conn_setup
+ if self.conn_setup.port <= 0:
+ self.conn_setup.port = PORT_SSL
self.id_ = uuid4()
self.log = Logger(self._log)
self.update_login(nick_confirmed=False,
nickname=self.conn_setup.nickname)
+ self.log.add(f'connecting {self.conn_setup}')
self.start_connecting()
def start_connecting(self) -> None:
def connect(self) -> None:
try:
self.conn = _IrcConnection(hostname=self.conn_setup.hostname,
+ port=self.conn_setup.port,
q_out=self.q_out,
client_id=self.id_)
self._cput(ClientEvent.make_subtype('on_connect'))
def on_connect(self) -> None:
'Steps to perform right after connection.'
- self.log.add(msg='connected to server', chat=CHAT_GLOB)
+ assert self.conn is not None
+ self.log.add('connected to server (SSL: '
+ f'{"yes" if self.conn.ssl else "no"})', chat=CHAT_GLOB)
self._caps.challenge('LS', '302')
self.send(IrcMessage(verb='USER',
params=(getuser(), '0', '*',
client_wins[0].prompt.prefix_copy_to(win.prompt)
return win
- def cmd__connect(self, hostname: str, nickname_and_pw: str, realname: str
+ def cmd__connect(self, host_port: str, nickname_pw: str, realname: str
) -> None:
'Create Client and pass it via NewClientEvent.'
- split = nickname_and_pw.split(':', maxsplit=1)
+ split = host_port.split(':', maxsplit=1)
+ hostname = split[0]
+ port = -1
+ if len(split) > 1:
+ to_int = split[1]
+ if to_int.isdigit():
+ port = int(split[1])
+ else:
+ self.log.alert(f'invalid port number: {to_int}')
+ return
+ split = nickname_pw.split(':', maxsplit=1)
nickname = split[0]
- pw = split[1] if len(split) > 1 else ''
+ password = split[1] if len(split) > 1 else ''
self._put(NewClientEvent(
_ClientKnowingTui(
q_out=self.q_out,
- conn_setup=IrcConnSetup(hostname, nickname, realname, pw))))
+ conn_setup=IrcConnSetup(
+ hostname, port, nickname, realname, password))))
@dataclass
# built-ins
from abc import ABC, abstractmethod
from socket import socket, gaierror as socket_gaierror
+from ssl import create_default_context as create_ssl_context
from typing import Callable, Iterator, NamedTuple, Optional, Self
# ourselves
from ircplom.events import Event, Loop, QueueMixin
+PORT_SSL = 6697
_TIMEOUT_RECV_LOOP = 0.1
_TIMEOUT_CONNECT = 5
_CONN_RECV_BUFSIZE = 1024
-_PORT = 6667
_IRCSPEC_LINE_SEPARATOR = b'\r\n'
_IRCSPEC_TAG_ESCAPES = ((r'\:', ';'),
class BaseIrcConnection(QueueMixin, ABC):
'Collects low-level server-client connection management.'
- def __init__(self, hostname: str, **kwargs) -> None:
+ def __init__(self, hostname: str, port: int, **kwargs) -> None:
super().__init__(**kwargs)
self._socket = socket()
+ self.ssl = port == PORT_SSL
+ if self.ssl:
+ self._socket = create_ssl_context().wrap_socket(
+ self._socket, server_hostname=hostname)
self._socket.settimeout(_TIMEOUT_CONNECT)
try:
- self._socket.connect((hostname, _PORT))
+ self._socket.connect((hostname, port))
except (TimeoutError, socket_gaierror) as e:
raise IrcConnAbortException(e) from e
self._socket.settimeout(_TIMEOUT_RECV_LOOP)