home · contact · privacy
Add port configuration, and wrap into SSL context if standard SSL port.
authorChristian Heller <c.heller@plomlompom.de>
Mon, 4 Aug 2025 21:01:10 +0000 (23:01 +0200)
committerChristian Heller <c.heller@plomlompom.de>
Mon, 4 Aug 2025 21:01:10 +0000 (23:01 +0200)
ircplom/client.py
ircplom/client_tui.py
ircplom/irc_conn.py

index 2b543a4c2c187c5adc8f4bb44884a22ce06ad8a9..caafc200f37f7959773b43fc97ab6355dd2f34e0 100644 (file)
@@ -11,7 +11,7 @@ from uuid import UUID, uuid4
 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 = '*'
@@ -167,6 +167,7 @@ class _CapsManager:
 class IrcConnSetup:
     'All we need to know to set up a new Client connection.'
     hostname: str
+    port: int
     nickname: str
     realname: str
     password: str
@@ -181,10 +182,13 @@ class Client(ABC, ClientQueueMixin):
         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:
@@ -193,6 +197,7 @@ class Client(ABC, ClientQueueMixin):
         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'))
@@ -205,7 +210,9 @@ class Client(ABC, ClientQueueMixin):
 
     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', '*',
index ab2450887b72e50d856333b9b08e58aa355988e7..c7e60c2fadd9698094e1a25c84ac69c63a50981c 100644 (file)
@@ -99,16 +99,27 @@ class ClientTui(BaseTui):
             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
index f7f5455adfe2398dda3bbdda8bdb8da23a542d2a..45ebc47a041faeed89c589ffb20be6f5d9f703c4 100644 (file)
@@ -2,15 +2,16 @@
 # 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'\:', ';'),
@@ -127,12 +128,16 @@ class IrcConnAbortException(Exception):
 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)