Add basic pre-registration SASL authentication.
Unfinished, currently breaks nickserv auto-identify when switching handles.
This commit is contained in:
parent
1ec2d42e31
commit
f8c3dd3b35
3 changed files with 73 additions and 15 deletions
55
irc.py
55
irc.py
|
@ -115,6 +115,8 @@ class PesterIRC(QtCore.QThread):
|
||||||
"768": self._keynotset,
|
"768": self._keynotset,
|
||||||
"769": self._keynopermission,
|
"769": self._keynopermission,
|
||||||
"770": self._metadatasubok,
|
"770": self._metadatasubok,
|
||||||
|
"903": self._saslsuccess, # We did a SASL!! woo yeah!! (RPL_SASLSUCCESS)
|
||||||
|
"904": self._saslfail, # oh no,,, cringe,,, (ERR_SASLFAIL)
|
||||||
"error": self._error,
|
"error": self._error,
|
||||||
"join": self._join,
|
"join": self._join,
|
||||||
"kick": self._kick,
|
"kick": self._kick,
|
||||||
|
@ -129,6 +131,7 @@ class PesterIRC(QtCore.QThread):
|
||||||
"metadata": self._metadata, # Metadata specification
|
"metadata": self._metadata, # Metadata specification
|
||||||
"tagmsg": self._tagmsg, # IRCv3 message tags extension
|
"tagmsg": self._tagmsg, # IRCv3 message tags extension
|
||||||
"cap": self._cap, # IRCv3 Client Capability Negotiation
|
"cap": self._cap, # IRCv3 Client Capability Negotiation
|
||||||
|
"authenticate": self._authenticate, # IRCv3 SASL authentication
|
||||||
}
|
}
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
@ -190,7 +193,28 @@ class PesterIRC(QtCore.QThread):
|
||||||
|
|
||||||
if self.password:
|
if self.password:
|
||||||
self._send_irc.pass_(self.password)
|
self._send_irc.pass_(self.password)
|
||||||
self._send_irc.nick(self.mainwindow.profile().handle)
|
|
||||||
|
# Negotiate capabilities
|
||||||
|
self._send_irc.cap("REQ", "message-tags")
|
||||||
|
self._send_irc.cap(
|
||||||
|
"REQ",
|
||||||
|
"draft/metadata-notify-2", # <--- Not required for the unreal5 module.
|
||||||
|
)
|
||||||
|
self._send_irc.cap("REQ", "pesterchum-tag") # <--- Currently not using this
|
||||||
|
self._send_irc.cap("REQ", "twitch.tv/membership") # Twitch silly
|
||||||
|
|
||||||
|
# This should not be here.
|
||||||
|
profile = self.mainwindow.profile()
|
||||||
|
# Do SASL!!
|
||||||
|
if self.mainwindow.userprofile.getAutoIdentify():
|
||||||
|
self._send_irc.cap("REQ", "sasl")
|
||||||
|
self._send_irc.authenticate("PLAIN")
|
||||||
|
else:
|
||||||
|
# Without SASL, end caps here.
|
||||||
|
self._send_irc.cap("END")
|
||||||
|
|
||||||
|
# Send NICK & USER :3
|
||||||
|
self._send_irc.nick(profile.handle)
|
||||||
self._send_irc.user("pcc31", "pcc31")
|
self._send_irc.user("pcc31", "pcc31")
|
||||||
|
|
||||||
def _conn_generator(self):
|
def _conn_generator(self):
|
||||||
|
@ -827,13 +851,6 @@ class PesterIRC(QtCore.QThread):
|
||||||
)
|
)
|
||||||
self.connected.emit() # Alert main thread that we've connected.
|
self.connected.emit() # Alert main thread that we've connected.
|
||||||
profile = self.mainwindow.profile()
|
profile = self.mainwindow.profile()
|
||||||
# Negotiate capabilities
|
|
||||||
self._send_irc.cap("REQ", "message-tags")
|
|
||||||
self._send_irc.cap(
|
|
||||||
"REQ", "draft/metadata-notify-2"
|
|
||||||
) # <--- Not required in the unreal5 module implementation
|
|
||||||
self._send_irc.cap("REQ", "pesterchum-tag") # <--- Currently not using this
|
|
||||||
self._send_irc.cap("REQ", "twitch.tv/membership") # Twitch silly
|
|
||||||
# Get mood
|
# Get mood
|
||||||
mood = profile.mood.value_str()
|
mood = profile.mood.value_str()
|
||||||
# Moods via metadata
|
# Moods via metadata
|
||||||
|
@ -867,8 +884,9 @@ class PesterIRC(QtCore.QThread):
|
||||||
See: https://ircv3.net/specs/extensions/capability-negotiation
|
See: https://ircv3.net/specs/extensions/capability-negotiation
|
||||||
"""
|
"""
|
||||||
PchumLog.info("CAP %s %s %s %s", server, nick, subcommand, tag)
|
PchumLog.info("CAP %s %s %s %s", server, nick, subcommand, tag)
|
||||||
# if tag == "message-tags":
|
if subcommand.casefold() == "nak" and tag.casefold() == "sasl":
|
||||||
# if subcommand == "ACK":
|
# SASL isn't supported, end CAP negotation.
|
||||||
|
self._send_irc.cap("END")
|
||||||
|
|
||||||
def _umodeis(self, _server, _handle, modes):
|
def _umodeis(self, _server, _handle, modes):
|
||||||
"""Numeric reply 221 RPL_UMODEIS, shows us our user modes."""
|
"""Numeric reply 221 RPL_UMODEIS, shows us our user modes."""
|
||||||
|
@ -1013,6 +1031,23 @@ class PesterIRC(QtCore.QThread):
|
||||||
""" "METADATA DRAFT numeric reply 770 RPL_METADATASUBOK, we subbed to a key."""
|
""" "METADATA DRAFT numeric reply 770 RPL_METADATASUBOK, we subbed to a key."""
|
||||||
PchumLog.info("_metadatasubok: %s", params)
|
PchumLog.info("_metadatasubok: %s", params)
|
||||||
|
|
||||||
|
def _authenticate(self, _, token):
|
||||||
|
"""Handle IRCv3 SASL authneticate command from server."""
|
||||||
|
if token == "+":
|
||||||
|
# Try to send password now
|
||||||
|
self._send_irc.authenticate(
|
||||||
|
nick=self.mainwindow.profile().handle,
|
||||||
|
password=self.mainwindow.userprofile.getNickServPass(),
|
||||||
|
)
|
||||||
|
|
||||||
|
def _saslfail(self, *_msg):
|
||||||
|
"""Handle 'RPL_SASLSUCCESS' reply from server, SASL authentication succeeded! woo yeah!!"""
|
||||||
|
self._send_irc.cap("END")
|
||||||
|
|
||||||
|
def _saslsuccess(self, *_msg):
|
||||||
|
"""Handle 'ERR_SASLFAIL' reply from server, SASL failed somehow."""
|
||||||
|
self._send_irc.cap("END")
|
||||||
|
|
||||||
moodUpdated = QtCore.pyqtSignal(str, Mood)
|
moodUpdated = QtCore.pyqtSignal(str, Mood)
|
||||||
colorUpdated = QtCore.pyqtSignal(str, QtGui.QColor)
|
colorUpdated = QtCore.pyqtSignal(str, QtGui.QColor)
|
||||||
messageReceived = QtCore.pyqtSignal(str, str)
|
messageReceived = QtCore.pyqtSignal(str, str)
|
||||||
|
|
|
@ -2545,10 +2545,11 @@ class PesterWindow(MovingWindow):
|
||||||
self.waitingMessages.answerMessage()
|
self.waitingMessages.answerMessage()
|
||||||
|
|
||||||
def doAutoIdentify(self):
|
def doAutoIdentify(self):
|
||||||
if self.userprofile.getAutoIdentify():
|
pass
|
||||||
self.sendMessage.emit(
|
# if self.userprofile.getAutoIdentify():
|
||||||
"identify " + self.userprofile.getNickServPass(), "NickServ"
|
# self.sendMessage.emit(
|
||||||
)
|
# "identify " + self.userprofile.getNickServPass(), "NickServ"
|
||||||
|
# )
|
||||||
|
|
||||||
def doAutoJoins(self):
|
def doAutoJoins(self):
|
||||||
if not self.autoJoinDone:
|
if not self.autoJoinDone:
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
"""IRC-related functions and classes to be imported by irc.py"""
|
"""IRC-related functions and classes to be imported by irc.py"""
|
||||||
import logging
|
import logging
|
||||||
|
import base64
|
||||||
|
|
||||||
PchumLog = logging.getLogger("pchumLogger")
|
PchumLog = logging.getLogger("pchumLogger")
|
||||||
|
|
||||||
|
@ -41,7 +42,7 @@ class SendIRC:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
PchumLog.debug("Sending: %s", command)
|
PchumLog.debug("Sending: %s", command)
|
||||||
self.socket.sendall(outgoing_bytes)
|
self.socket.send(outgoing_bytes)
|
||||||
except OSError:
|
except OSError:
|
||||||
PchumLog.exception("Error while sending: '%s'", command.strip())
|
PchumLog.exception("Error while sending: '%s'", command.strip())
|
||||||
self.socket.close()
|
self.socket.close()
|
||||||
|
@ -157,6 +158,27 @@ class SendIRC:
|
||||||
"""Send QUIT to terminate connection."""
|
"""Send QUIT to terminate connection."""
|
||||||
self._send("QUIT", text=reason)
|
self._send("QUIT", text=reason)
|
||||||
|
|
||||||
|
def authenticate(self, token=None, nick=None, password=None):
|
||||||
|
"""Authenticate command for SASL.
|
||||||
|
|
||||||
|
Send either a token like 'PLAIN' or authenticate with nick and password.
|
||||||
|
|
||||||
|
Reference: https://ircv3.net/irc/#account-authentication-and-registration
|
||||||
|
"""
|
||||||
|
if token:
|
||||||
|
self._send("AUTHENTICATE", text=token)
|
||||||
|
return
|
||||||
|
if nick and password:
|
||||||
|
# Authentication identity 'nick', authorization identity 'nick' and password 'password'.
|
||||||
|
sasl_string = f"{nick}\x00{nick}\x00{password}"
|
||||||
|
# Encode to use base64, then decode since 'text' only takes str.
|
||||||
|
sasl_string_bytes = sasl_string.encode(encoding="utf-8", errors="replace")
|
||||||
|
sasl_string_base64 = base64.b64encode(sasl_string_bytes).decode(
|
||||||
|
encoding="utf-8"
|
||||||
|
)
|
||||||
|
# Woo yeah woo yeah
|
||||||
|
self._send("AUTHENTICATE", text=sasl_string_base64)
|
||||||
|
|
||||||
|
|
||||||
def parse_irc_line(line: str):
|
def parse_irc_line(line: str):
|
||||||
"""Retrieves tags, prefix, command, and arguments from an unparsed IRC line."""
|
"""Retrieves tags, prefix, command, and arguments from an unparsed IRC line."""
|
||||||
|
|
Loading…
Reference in a new issue