Wed, 30 Dec 2020 11:00:05 +0100
Updated copyright for 2021.
# -*- coding: utf-8 -*- # Copyright (c) 2012 - 2021 Detlev Offenbach <detlev@die-offenbachs.de> # """ Module implementing the IRC window. """ import re import logging from PyQt5.QtCore import ( pyqtSlot, pyqtSignal, Qt, QByteArray, QTimer, QDateTime ) from PyQt5.QtWidgets import QWidget, QToolButton, QLabel, QTabWidget from PyQt5.QtNetwork import QTcpSocket, QAbstractSocket try: from PyQt5.QtNetwork import QSslSocket, QSslConfiguration from E5Network.E5SslErrorHandler import E5SslErrorHandler SSL_AVAILABLE = True except ImportError: SSL_AVAILABLE = False from E5Gui import E5MessageBox from .Ui_IrcWidget import Ui_IrcWidget import Preferences import UI.PixmapCache from Globals import isMacPlatform from UI.Info import Version, Copyright class IrcWidget(QWidget, Ui_IrcWidget): """ Class implementing the IRC window. @signal autoConnected() emitted after an automatic connection was initiated """ autoConnected = pyqtSignal() ServerDisconnected = 1 ServerConnected = 2 ServerConnecting = 3 def __init__(self, parent=None): """ Constructor @param parent reference to the parent widget (QWidget) """ super(IrcWidget, self).__init__(parent) self.setupUi(self) from .IrcNetworkManager import IrcNetworkManager self.__ircNetworkManager = IrcNetworkManager(self) self.__leaveButton = QToolButton(self) self.__leaveButton.setIcon( UI.PixmapCache.getIcon("ircCloseChannel")) self.__leaveButton.setToolTip( self.tr("Press to leave the current channel")) self.__leaveButton.clicked.connect(self.__leaveChannel) self.__leaveButton.setEnabled(False) self.channelsWidget.setCornerWidget( self.__leaveButton, Qt.BottomRightCorner) self.channelsWidget.setTabsClosable(False) if not isMacPlatform(): self.channelsWidget.setTabPosition(QTabWidget.South) height = self.height() self.splitter.setSizes([height * 0.6, height * 0.4]) self.__channelList = [] self.__channelTypePrefixes = "" self.__userName = "" self.__identityName = "" self.__quitMessage = "" self.__nickIndex = -1 self.__nickName = "" self.__server = None self.__registering = False self.__connectionState = IrcWidget.ServerDisconnected self.__sslErrorLock = False self.__buffer = "" self.__userPrefix = {} self.__socket = None if SSL_AVAILABLE: self.__sslErrorHandler = E5SslErrorHandler(self) else: self.__sslErrorHandler = None self.__patterns = [ # :foo_!n=foo@foohost.bar.net PRIVMSG bar_ :some long message (re.compile(r":([^!]+)!([^ ]+)\sPRIVMSG\s([^ ]+)\s:(.*)"), self.__query), # :foo.bar.net COMMAND some message (re.compile(r""":([^ ]+)\s+([A-Z]+)\s+(.+)"""), self.__handleNamedMessage), # :foo.bar.net 123 * :info (re.compile(r""":([^ ]+)\s+(\d{3})\s+(.+)"""), self.__handleNumericMessage), # PING :ping message (re.compile(r"""PING\s+:(.*)"""), self.__ping), ] self.__prefixRe = re.compile(r""".*\sPREFIX=\((.*)\)([^ ]+).*""") self.__chanTypesRe = re.compile(r""".*\sCHANTYPES=([^ ]+).*""") ircPic = UI.PixmapCache.getPixmap("irc128") self.__emptyLabel = QLabel() self.__emptyLabel.setPixmap(ircPic) self.__emptyLabel.setAlignment(Qt.AlignVCenter | Qt.AlignHCenter) self.channelsWidget.addTab(self.__emptyLabel, "") # all initialized, do connections now self.__ircNetworkManager.dataChanged.connect(self.__networkDataChanged) self.networkWidget.initialize(self.__ircNetworkManager) self.networkWidget.connectNetwork.connect(self.__connectNetwork) self.networkWidget.editNetwork.connect(self.__editNetwork) self.networkWidget.joinChannel.connect(self.joinChannel) self.networkWidget.nickChanged.connect(self.__changeNick) self.networkWidget.sendData.connect(self.__send) self.networkWidget.away.connect(self.__away) self.networkWidget.autoConnected.connect(self.autoConnected) def shutdown(self): """ Public method to shut down the widget. @return flag indicating successful shutdown (boolean) """ if self.__server: if Preferences.getIrc("AskOnShutdown"): ok = E5MessageBox.yesNo( self, self.tr("Disconnect from Server"), self.tr( """<p>Do you really want to disconnect from""" """ <b>{0}</b>?</p><p>All channels will be closed.""" """</p>""").format(self.__server.getName())) else: ok = True if ok: self.__connectNetwork("", False, True) else: ok = True if ok: self.__ircNetworkManager.close() return ok def autoConnect(self): """ Public method to initiate the IRC auto connection. """ self.networkWidget.autoConnect() def __connectNetwork(self, name, connect, silent): """ Private slot to connect to or disconnect from the given network. @param name name of the network to connect to (string) @param connect flag indicating to connect (boolean) @param silent flag indicating a silent connect/disconnect (boolean) """ if connect: network = self.__ircNetworkManager.getNetwork(name) if network: self.__server = network.getServer() self.__identityName = network.getIdentityName() identity = self.__ircNetworkManager.getIdentity( self.__identityName) if identity: self.__userName = identity.getIdent() self.__quitMessage = identity.getQuitMessage() if self.__server: useSSL = self.__server.useSSL() if useSSL and not SSL_AVAILABLE: E5MessageBox.critical( self, self.tr("SSL Connection"), self.tr( """An encrypted connection to the IRC""" """ network was requested but SSL is not""" """ available. Please change the server""" """ configuration.""")) return if useSSL: # create SSL socket self.__socket = QSslSocket(self) self.__socket.encrypted.connect( self.__hostConnected) self.__socket.sslErrors.connect( self.__sslErrors) else: # create TCP socket self.__socket = QTcpSocket(self) self.__socket.connected.connect( self.__hostConnected) self.__socket.hostFound.connect( self.__hostFound) self.__socket.disconnected.connect( self.__hostDisconnected) self.__socket.readyRead.connect( self.__readyRead) self.__socket.error.connect( self.__tcpError) self.__connectionState = IrcWidget.ServerConnecting if useSSL: self.networkWidget.addServerMessage( self.tr("Info"), self.tr("Looking for server {0} (port {1})" " using an SSL encrypted connection" "...").format(self.__server.getName(), self.__server.getPort())) self.__socket.connectToHostEncrypted( self.__server.getName(), self.__server.getPort() ) else: self.networkWidget.addServerMessage( self.tr("Info"), self.tr( "Looking for server {0} (port {1})...") .format( self.__server.getName(), self.__server.getPort())) self.__socket.connectToHost( self.__server.getName(), self.__server.getPort()) else: if silent: ok = True else: ok = E5MessageBox.yesNo( self, self.tr("Disconnect from Server"), self.tr("""<p>Do you really want to disconnect from""" """ <b>{0}</b>?</p><p>All channels will be""" """ closed.</p>""") .format(self.__server.getName())) if ok: if self.__server is not None: self.networkWidget.addServerMessage( self.tr("Info"), self.tr("Disconnecting from server {0}...").format( self.__server.getName())) elif name: self.networkWidget.addServerMessage( self.tr("Info"), self.tr("Disconnecting from network {0}...").format( name)) else: self.networkWidget.addServerMessage( self.tr("Info"), self.tr("Disconnecting from server.")) self.__closeAllChannels() self.__send("QUIT :" + self.__quitMessage) if self.__socket: self.__socket.flush() self.__socket.close() if self.__socket: # socket is still existing self.__socket.deleteLater() self.__socket = None self.__userName = "" self.__identityName = "" self.__quitMessage = "" def __editNetwork(self, name): """ Private slot to edit the network configuration. @param name name of the network to edit (string) """ from .IrcNetworkListDialog import IrcNetworkListDialog dlg = IrcNetworkListDialog(self.__ircNetworkManager, self) dlg.exec() def __networkDataChanged(self): """ Private slot handling changes of the network and identity definitions. """ identity = self.__ircNetworkManager.getIdentity(self.__identityName) if identity: partMsg = identity.getPartMessage() for channel in self.__channelList: channel.setPartMessage(partMsg) def joinChannel(self, name, key=""): """ Public slot to join a channel. @param name name of the channel (string) @param key key of the channel (string) """ # step 1: check, if this channel is already joined for channel in self.__channelList: if channel.name() == name: return from .IrcChannelWidget import IrcChannelWidget channel = IrcChannelWidget(self) channel.setName(name) channel.setUserName(self.__nickName) identity = self.__ircNetworkManager.getIdentity(self.__identityName) if identity: channel.setPartMessage(identity.getPartMessage()) channel.setUserPrivilegePrefix(self.__userPrefix) channel.initAutoWho() channel.sendData.connect(self.__send) channel.sendCtcpRequest.connect(self.__sendCtcpRequest) channel.sendCtcpReply.connect(self.__sendCtcpReply) channel.channelClosed.connect(self.__closeChannel) channel.openPrivateChat.connect(self.__openPrivate) channel.awayCommand.connect(self.networkWidget.handleAwayCommand) channel.leaveChannels.connect(self.__leaveChannels) channel.leaveAllChannels.connect(self.__leaveAllChannels) self.channelsWidget.addTab(channel, name) self.__channelList.append(channel) self.channelsWidget.setCurrentWidget(channel) joinCommand = ["JOIN", name] if key: joinCommand.append(key) self.__send(" ".join(joinCommand)) self.__send("MODE " + name) emptyIndex = self.channelsWidget.indexOf(self.__emptyLabel) if emptyIndex > -1: self.channelsWidget.removeTab(emptyIndex) self.__leaveButton.setEnabled(True) self.channelsWidget.setTabsClosable(True) def __query(self, match): """ Private method to handle a new private connection. @param match reference to the match object @return flag indicating, if the message was handled (boolean) """ # group(1) sender user name # group(2) sender user@host # group(3) target nick # group(4) message if match.group(4).startswith("\x01"): return self.__handleCtcp(match) self.__openPrivate(match.group(1)) # the above call sets the new channel as the current widget channel = self.channelsWidget.currentWidget() channel.addMessage(match.group(1), match.group(4)) channel.setPrivateInfo( "{0} - {1}".format(match.group(1), match.group(2))) return True @pyqtSlot(str) def __openPrivate(self, name): """ Private slot to open a private chat with the given user. @param name name of the user (string) """ from .IrcChannelWidget import IrcChannelWidget channel = IrcChannelWidget(self) channel.setName(self.__nickName) channel.setUserName(self.__nickName) identity = self.__ircNetworkManager.getIdentity(self.__identityName) if identity: channel.setPartMessage(identity.getPartMessage()) channel.setUserPrivilegePrefix(self.__userPrefix) channel.setPrivate(True, name) channel.addUsers([name, self.__nickName]) channel.sendData.connect(self.__send) channel.sendCtcpRequest.connect(self.__sendCtcpRequest) channel.sendCtcpReply.connect(self.__sendCtcpReply) channel.channelClosed.connect(self.__closeChannel) channel.awayCommand.connect(self.networkWidget.handleAwayCommand) channel.leaveChannels.connect(self.__leaveChannels) channel.leaveAllChannels.connect(self.__leaveAllChannels) self.channelsWidget.addTab(channel, name) self.__channelList.append(channel) self.channelsWidget.setCurrentWidget(channel) @pyqtSlot() def __leaveChannel(self): """ Private slot to leave a channel and close the associated tab. """ channel = self.channelsWidget.currentWidget() channel.requestLeave() @pyqtSlot(list) def __leaveChannels(self, channelNames): """ Private slot to leave a list of channels and close their associated tabs. @param channelNames list of channels to leave @type list of str """ for channelName in channelNames: for channel in self.__channelList: if channel.name() == channelName: channel.leaveChannel() @pyqtSlot() def __leaveAllChannels(self): """ Private slot to leave all channels and close their tabs. """ while self.__channelList: channel = self.__channelList[0] channel.leaveChannel() def __closeAllChannels(self): """ Private method to close all channels. """ while self.__channelList: channel = self.__channelList.pop() self.channelsWidget.removeTab(self.channelsWidget.indexOf(channel)) channel.deleteLater() channel = None self.channelsWidget.addTab(self.__emptyLabel, "") self.__emptyLabel.show() self.__leaveButton.setEnabled(False) self.channelsWidget.setTabsClosable(False) def __closeChannel(self, name): """ Private slot handling the closing of a channel. @param name name of the closed channel (string) """ for channel in self.__channelList: if channel.name() == name: self.channelsWidget.removeTab( self.channelsWidget.indexOf(channel)) self.__channelList.remove(channel) channel.deleteLater() if self.channelsWidget.count() == 0: self.channelsWidget.addTab(self.__emptyLabel, "") self.__emptyLabel.show() self.__leaveButton.setEnabled(False) self.channelsWidget.setTabsClosable(False) @pyqtSlot(int) def on_channelsWidget_tabCloseRequested(self, index): """ Private slot to close a channel by pressing the close button of the channels widget. @param index index of the tab to be closed (integer) """ channel = self.channelsWidget.widget(index) channel.requestLeave() def __send(self, data): """ Private slot to send data to the IRC server. @param data data to be sent (string) """ if self.__socket: self.__socket.write( QByteArray("{0}\r\n".format(data).encode("utf-8"))) def __sendCtcpRequest(self, receiver, request, arguments): """ Private slot to send a CTCP request. @param receiver nick name of the receiver @type str @param request CTCP request to be sent @type str @param arguments arguments to be sent @type str """ request = request.upper() if request == "PING": arguments = "Eric IRC {0}".format( QDateTime.currentMSecsSinceEpoch()) self.__send("PRIVMSG {0} :\x01{1} {2}\x01".format( receiver, request, arguments)) def __sendCtcpReply(self, receiver, text): """ Private slot to send a CTCP reply. @param receiver nick name of the receiver @type str @param text text to be sent @type str """ self.__send("NOTICE {0} :\x01{1}\x01".format(receiver, text)) def __hostFound(self): """ Private slot to indicate the host was found. """ self.networkWidget.addServerMessage( self.tr("Info"), self.tr("Server found,connecting...")) def __hostConnected(self): """ Private slot to log in to the server after the connection was established. """ self.networkWidget.addServerMessage( self.tr("Info"), self.tr("Connected,logging in...")) self.networkWidget.setConnected(True) self.__registering = True serverPassword = self.__server.getPassword() if serverPassword: self.__send("PASS " + serverPassword) identity = self.__ircNetworkManager.getIdentity( self.__identityName) nick = self.networkWidget.getNickname() if not nick and identity: self.__nickIndex = 0 try: nick = identity.getNickNames()[self.__nickIndex] except IndexError: nick = "" if not nick: nick = self.__userName self.__nickName = nick self.networkWidget.setNickName(nick) if identity: realName = identity.getRealName() if not realName: realName = "eric IDE chat" self.__send("NICK " + nick) self.__send("USER " + self.__userName + " 0 * :" + realName) def __hostDisconnected(self): """ Private slot to indicate the host was disconnected. """ if self.networkWidget.isConnected(): self.__closeAllChannels() self.networkWidget.addServerMessage( self.tr("Info"), self.tr("Server disconnected.")) self.networkWidget.setRegistered(False) self.networkWidget.setConnected(False) self.__server = None self.__nickName = "" self.__nickIndex = -1 self.__channelTypePrefixes = "" if self.__socket: self.__socket.deleteLater() self.__socket = None self.__connectionState = IrcWidget.ServerDisconnected self.__sslErrorLock = False def __readyRead(self): """ Private slot to read data from the socket. """ if self.__socket: self.__buffer += str( self.__socket.readAll(), Preferences.getSystem("IOEncoding"), 'replace') if self.__buffer.endswith("\r\n"): for line in self.__buffer.splitlines(): line = line.strip() if line: logging.debug("<IRC> %s", line) handled = False # step 1: give channels a chance to handle the message for channel in self.__channelList: handled = channel.handleMessage(line) if handled: break else: # step 2: try to process the message ourselves for patternRe, patternFunc in self.__patterns: match = patternRe.match(line) if match is not None: if patternFunc(match): break else: # Oops, the message wasn't handled self.networkWidget.addErrorMessage( self.tr("Message Error"), self.tr( "Unknown message received from server:" "<br/>{0}").format(line)) self.__updateUsersCount() self.__buffer = "" def __handleCtcpReply(self, match): """ Private method to handle a server message containing a CTCP reply. @param match reference to the match object """ if "!" in match.group(1): sender = match.group(1).split("!", 1)[0] try: ctcpCommand = match.group(3).split(":", 1)[1] except IndexError: ctcpCommand = match.group(3) ctcpCommand = ctcpCommand[1:].split("\x01", 1)[0] if " " in ctcpCommand: ctcpReply, ctcpArg = ctcpCommand.split(" ", 1) else: ctcpReply, ctcpArg = ctcpCommand, "" ctcpReply = ctcpReply.upper() if ctcpReply == "PING" and ctcpArg.startswith("Eric IRC "): # it is a response to a ping request pingDateTime = int(ctcpArg.split()[-1]) latency = QDateTime.currentMSecsSinceEpoch() - pingDateTime self.networkWidget.addServerMessage( self.tr("CTCP"), self.tr( "Received CTCP-PING response from {0} with latency" " of {1} ms.").format(sender, latency)) else: self.networkWidget.addServerMessage( self.tr("CTCP"), self.tr( "Received unknown CTCP-{0} response from {1}.") .format(ctcpReply, sender)) def __handleNamedMessage(self, match): """ Private method to handle a server message containing a message name. @param match reference to the match object @return flag indicating, if the message was handled (boolean) """ name = match.group(2) if name == "NOTICE": try: msg = match.group(3).split(":", 1)[1] except IndexError: msg = match.group(3) if msg.startswith("\x01"): self.__handleCtcpReply(match) return True if "!" in match.group(1): name = match.group(1).split("!", 1)[0] msg = "-{0}- {1}".format(name, msg) self.networkWidget.addServerMessage(self.tr("Notice"), msg) return True elif name == "MODE": self.__registering = False if ":" in match.group(3): # :foo MODE foo :+i name, modes = match.group(3).split(" :") sourceNick = match.group(1) if not self.isChannelName(name): if name == self.__nickName: if sourceNick == self.__nickName: msg = self.tr( "You have set your personal modes to" " <b>[{0}]</b>.").format(modes) else: msg = self.tr( "{0} has changed your personal modes to" " <b>[{1}]</b>.").format(sourceNick, modes) self.networkWidget.addServerMessage( self.tr("Mode"), msg, filterMsg=False) return True elif name == "PART": nick = match.group(1).split("!", 1)[0] if nick == self.__nickName: channel = match.group(3).split(None, 1)[0] self.networkWidget.addMessage( self.tr("You have left channel {0}.").format(channel)) return True elif name == "QUIT": # don't do anything with it here return True elif name == "NICK": # :foo_!n=foo@foohost.bar.net NICK :newnick oldNick = match.group(1).split("!", 1)[0] newNick = match.group(3).split(":", 1)[1] if oldNick == self.__nickName: self.networkWidget.addMessage( self.tr("You are now known as {0}.").format(newNick)) self.__nickName = newNick self.networkWidget.setNickName(newNick) else: self.networkWidget.addMessage( self.tr("User {0} is now known as {1}.").format( oldNick, newNick)) return True elif name == "PONG": nick = match.group(3).split(":", 1)[1] self.networkWidget.addMessage( self.tr("Received PONG from {0}").format(nick)) return True elif name == "ERROR": self.networkWidget.addErrorMessage( self.tr("Server Error"), match.group(3).split(":", 1)[1]) return True return False def __handleNumericMessage(self, match): """ Private method to handle a server message containing a numeric code. @param match reference to the match object @return flag indicating, if the message was handled (boolean) """ code = int(match.group(2)) if code < 400: return self.__handleServerReply( code, match.group(1), match.group(3)) else: return self.__handleServerError( code, match.group(1), match.group(3)) def __handleServerError(self, code, server, message): """ Private slot to handle a server error reply. @param code numerical code sent by the server (integer) @param server name of the server (string) @param message message sent by the server (string) @return flag indicating, if the message was handled (boolean) """ if code == 433: if self.__registering: self.__handleNickInUseLogin() else: self.__handleNickInUse() else: self.networkWidget.addServerMessage(self.tr("Error"), message) return True def __handleServerReply(self, code, server, message): """ Private slot to handle a server reply. @param code numerical code sent by the server (integer) @param server name of the server (string) @param message message sent by the server (string) @return flag indicating, if the message was handled (boolean) """ # determine message type if code in [1, 2, 3, 4]: msgType = self.tr("Welcome") elif code == 5: msgType = self.tr("Support") elif code in [250, 251, 252, 253, 254, 255, 265, 266]: msgType = self.tr("User") elif code in [372, 375, 376]: msgType = self.tr("MOTD") elif code in [305, 306]: msgType = self.tr("Away") else: msgType = self.tr("Info ({0})").format(code) # special treatment for some messages if code == 375: message = self.tr("Message of the day") elif code == 376: message = self.tr("End of message of the day") elif code == 4: parts = message.strip().split() message = self.tr( "Server {0} (Version {1}), User-Modes: {2}," " Channel-Modes: {3}" ).format(parts[1], parts[2], parts[3], parts[4]) elif code == 265: parts = message.strip().split() message = self.tr( "Current users on {0}: {1}, max. {2}").format( server, parts[1], parts[2]) elif code == 266: parts = message.strip().split() message = self.tr( "Current users on the network: {0}, max. {1}").format( parts[1], parts[2]) elif code == 305: message = self.tr("You are no longer marked as being away.") elif code == 306: message = self.tr("You have been marked as being away.") else: first, message = message.split(None, 1) if message.startswith(":"): message = message[1:] else: message = message.replace(":", "", 1) self.networkWidget.addServerMessage(msgType, message) if code == 1: # register with services after the welcome message self.__connectionState = IrcWidget.ServerConnected self.__registerWithServices() self.networkWidget.setRegistered(True) QTimer.singleShot(1000, self.__autoJoinChannels) elif code == 5: # extract the user privilege prefixes # ... PREFIX=(ov)@+ ... m = self.__prefixRe.match(message) if m: self.__setUserPrivilegePrefix(m.group(1), m.group(2)) # extract the channel type prefixes # ... CHANTYPES=# ... m = self.__chanTypesRe.match(message) if m: self.__setChannelTypePrefixes(m.group(1)) return True def __registerWithServices(self): """ Private method to register to services. """ identity = self.__ircNetworkManager.getIdentity(self.__identityName) if identity: service = identity.getServiceName() password = identity.getPassword() if service and password: self.__send("PRIVMSG " + service + " :identify " + password) def __autoJoinChannels(self): """ Private slot to join channels automatically once a server got connected. """ for channel in self.networkWidget.getNetworkChannels(): if channel.autoJoin(): name = channel.getName() key = channel.getKey() self.joinChannel(name, key) def __tcpError(self, error): """ Private slot to handle errors reported by the TCP socket. @param error error code reported by the socket (QAbstractSocket.SocketError) """ if error == QAbstractSocket.RemoteHostClosedError: # ignore this one, it's a disconnect if self.__sslErrorLock: self.networkWidget.addErrorMessage( self.tr("SSL Error"), self.tr( """Connection to server {0} (port {1}) lost while""" """ waiting for user response to an SSL error.""") .format(self.__server.getName(), self.__server.getPort())) self.__connectionState = IrcWidget.ServerDisconnected elif error == QAbstractSocket.HostNotFoundError: self.networkWidget.addErrorMessage( self.tr("Socket Error"), self.tr( "The host was not found. Please check the host name" " and port settings.")) elif error == QAbstractSocket.ConnectionRefusedError: self.networkWidget.addErrorMessage( self.tr("Socket Error"), self.tr( "The connection was refused by the peer. Please check the" " host name and port settings.")) elif error == QAbstractSocket.SslHandshakeFailedError: self.networkWidget.addErrorMessage( self.tr("Socket Error"), self.tr("The SSL handshake failed.")) else: if self.__socket: self.networkWidget.addErrorMessage( self.tr("Socket Error"), self.tr( "The following network error occurred:<br/>{0}") .format(self.__socket.errorString())) else: self.networkWidget.addErrorMessage( self.tr("Socket Error"), self.tr("A network error occurred.")) def __sslErrors(self, errors): """ Private slot to handle SSL errors. @param errors list of SSL errors (list of QSslError) """ ignored, defaultChanged = self.__sslErrorHandler.sslErrors( errors, self.__server.getName(), self.__server.getPort()) if ignored == E5SslErrorHandler.NotIgnored: self.networkWidget.addErrorMessage( self.tr("SSL Error"), self.tr( """Could not connect to {0} (port {1}) using an SSL""" """ encrypted connection. Either the server does not""" """ support SSL (did you use the correct port?) or""" """ you rejected the certificate.""") .format(self.__server.getName(), self.__server.getPort())) self.__socket.close() else: if defaultChanged: self.__socket.setSslConfiguration( QSslConfiguration.defaultConfiguration()) if ignored == E5SslErrorHandler.UserIgnored: self.networkWidget.addErrorMessage( self.tr("SSL Error"), self.tr( """The SSL certificate for the server {0} (port {1})""" """ failed the authenticity check. SSL errors""" """ were accepted by you.""") .format(self.__server.getName(), self.__server.getPort())) if self.__connectionState == IrcWidget.ServerConnecting: self.__socket.ignoreSslErrors() def __setUserPrivilegePrefix(self, prefix1, prefix2): """ Private method to set the user privilege prefix. @param prefix1 first part of the prefix (string) @param prefix2 indictors the first part gets mapped to (string) """ # PREFIX=(ov)@+ # o = @ -> @ircbot , channel operator # v = + -> +userName , voice operator for i in range(len(prefix1)): self.__userPrefix["+" + prefix1[i]] = prefix2[i] self.__userPrefix["-" + prefix1[i]] = "" def __ping(self, match): """ Private method to handle a PING message. @param match reference to the match object @return flag indicating, if the message was handled (boolean) """ self.__send("PONG " + match.group(1)) return True def __handleCtcp(self, match): """ Private method to handle a CTCP command. @param match reference to the match object @return flag indicating, if the message was handled (boolean) """ # group(1) sender user name # group(2) sender user@host # group(3) target nick # group(4) message if match.group(4).startswith("\x01"): ctcpCommand = match.group(4)[1:].split("\x01", 1)[0] if " " in ctcpCommand: ctcpRequest, ctcpArg = ctcpCommand.split(" ", 1) else: ctcpRequest, ctcpArg = ctcpCommand, "" ctcpRequest = ctcpRequest.lower() if ctcpRequest == "version": if Version.startswith("@@"): vers = "" else: vers = " " + Version msg = "Eric IRC client{0}, {1}".format(vers, Copyright) self.networkWidget.addServerMessage( self.tr("CTCP"), self.tr("Received Version request from {0}.").format( match.group(1))) self.__sendCtcpReply(match.group(1), "VERSION " + msg) elif ctcpRequest == "ping": self.networkWidget.addServerMessage( self.tr("CTCP"), self.tr( "Received CTCP-PING request from {0}," " sending answer.").format(match.group(1))) self.__sendCtcpReply( match.group(1), "PING {0}".format(ctcpArg)) elif ctcpRequest == "clientinfo": self.networkWidget.addServerMessage( self.tr("CTCP"), self.tr( "Received CTCP-CLIENTINFO request from {0}," " sending answer.").format(match.group(1))) self.__sendCtcpReply( match.group(1), "CLIENTINFO CLIENTINFO PING VERSION") else: self.networkWidget.addServerMessage( self.tr("CTCP"), self.tr( "Received unknown CTCP-{0} request from {1}.") .format(ctcpRequest, match.group(1))) return True return False def __updateUsersCount(self): """ Private method to update the users count on the channel tabs. """ for channel in self.__channelList: index = self.channelsWidget.indexOf(channel) self.channelsWidget.setTabText( index, self.tr("{0} ({1})", "channel name, users count").format( channel.name(), channel.getUsersCount())) def __handleNickInUseLogin(self): """ Private method to handle a 443 server error at login. """ self.__nickIndex += 1 try: identity = self.__ircNetworkManager.getIdentity( self.__identityName) if identity: nick = identity.getNickNames()[self.__nickIndex] self.__nickName = nick else: self.__connectNetwork("", False, True) self.__nickName = "" self.__nickIndex = -1 return except IndexError: self.networkWidget.addServerMessage( self.tr("Critical"), self.tr( "No nickname acceptable to the server configured" " for <b>{0}</b>. Disconnecting...") .format(self.__userName), filterMsg=False) self.__connectNetwork("", False, True) self.__nickName = "" self.__nickIndex = -1 return self.networkWidget.setNickName(nick) self.__send("NICK " + nick) def __handleNickInUse(self): """ Private method to handle a 443 server error. """ self.networkWidget.addServerMessage( self.tr("Critical"), self.tr("The given nickname is already in use.")) def __changeNick(self, nick): """ Private slot to use a new nick name. @param nick nick name to use (str) """ if nick and nick != self.__nickName: self.__send("NICK " + nick) def __setChannelTypePrefixes(self, prefixes): """ Private method to set the channel type prefixes. @param prefixes channel prefix characters (string) """ self.__channelTypePrefixes = prefixes def isChannelName(self, name): """ Public method to check, if the given name is a channel name. @param name name to check (string) @return flag indicating a channel name (boolean) """ if not name: return False if self.__channelTypePrefixes: return name[0] in self.__channelTypePrefixes else: return name[0] in "#&" def __away(self, isAway): """ Private slot handling the change of the away state. @param isAway flag indicating the current away state (boolean) """ if isAway and self.__identityName: identity = self.__ircNetworkManager.getIdentity( self.__identityName) if identity and identity.rememberAwayPosition(): for channel in self.__channelList: channel.setMarkerLine()