src/eric7/Network/IRC/IrcWidget.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9413
80c06d472826
equal deleted inserted replaced
9220:e9e7eca7efee 9221:bf71ee032bb4
8 """ 8 """
9 9
10 import re 10 import re
11 import logging 11 import logging
12 12
13 from PyQt6.QtCore import ( 13 from PyQt6.QtCore import pyqtSlot, pyqtSignal, Qt, QByteArray, QTimer, QDateTime
14 pyqtSlot, pyqtSignal, Qt, QByteArray, QTimer, QDateTime
15 )
16 from PyQt6.QtWidgets import QWidget, QToolButton, QLabel, QTabWidget 14 from PyQt6.QtWidgets import QWidget, QToolButton, QLabel, QTabWidget
17 from PyQt6.QtNetwork import QTcpSocket, QAbstractSocket 15 from PyQt6.QtNetwork import QTcpSocket, QAbstractSocket
16
18 try: 17 try:
19 from PyQt6.QtNetwork import QSslSocket, QSslConfiguration 18 from PyQt6.QtNetwork import QSslSocket, QSslConfiguration
20 from EricNetwork.EricSslErrorHandler import ( 19 from EricNetwork.EricSslErrorHandler import EricSslErrorHandler, EricSslErrorState
21 EricSslErrorHandler, EricSslErrorState 20
22 )
23 SSL_AVAILABLE = True 21 SSL_AVAILABLE = True
24 except ImportError: 22 except ImportError:
25 SSL_AVAILABLE = False 23 SSL_AVAILABLE = False
26 24
27 from EricWidgets import EricMessageBox 25 from EricWidgets import EricMessageBox
37 35
38 36
39 class IrcWidget(QWidget, Ui_IrcWidget): 37 class IrcWidget(QWidget, Ui_IrcWidget):
40 """ 38 """
41 Class implementing the IRC window. 39 Class implementing the IRC window.
42 40
43 @signal autoConnected() emitted after an automatic connection was initiated 41 @signal autoConnected() emitted after an automatic connection was initiated
44 """ 42 """
43
45 autoConnected = pyqtSignal() 44 autoConnected = pyqtSignal()
46 45
47 ServerDisconnected = 1 46 ServerDisconnected = 1
48 ServerConnected = 2 47 ServerConnected = 2
49 ServerConnecting = 3 48 ServerConnecting = 3
50 49
51 def __init__(self, parent=None): 50 def __init__(self, parent=None):
52 """ 51 """
53 Constructor 52 Constructor
54 53
55 @param parent reference to the parent widget (QWidget) 54 @param parent reference to the parent widget (QWidget)
56 """ 55 """
57 super().__init__(parent) 56 super().__init__(parent)
58 self.setupUi(self) 57 self.setupUi(self)
59 58
60 from .IrcNetworkManager import IrcNetworkManager 59 from .IrcNetworkManager import IrcNetworkManager
60
61 self.__ircNetworkManager = IrcNetworkManager(self) 61 self.__ircNetworkManager = IrcNetworkManager(self)
62 62
63 self.__leaveButton = QToolButton(self) 63 self.__leaveButton = QToolButton(self)
64 self.__leaveButton.setIcon( 64 self.__leaveButton.setIcon(UI.PixmapCache.getIcon("ircCloseChannel"))
65 UI.PixmapCache.getIcon("ircCloseChannel")) 65 self.__leaveButton.setToolTip(self.tr("Press to leave the current channel"))
66 self.__leaveButton.setToolTip(
67 self.tr("Press to leave the current channel"))
68 self.__leaveButton.clicked.connect(self.__leaveChannel) 66 self.__leaveButton.clicked.connect(self.__leaveChannel)
69 self.__leaveButton.setEnabled(False) 67 self.__leaveButton.setEnabled(False)
70 self.channelsWidget.setCornerWidget( 68 self.channelsWidget.setCornerWidget(
71 self.__leaveButton, Qt.Corner.BottomRightCorner) 69 self.__leaveButton, Qt.Corner.BottomRightCorner
70 )
72 self.channelsWidget.setTabsClosable(False) 71 self.channelsWidget.setTabsClosable(False)
73 if not isMacPlatform(): 72 if not isMacPlatform():
74 self.channelsWidget.setTabPosition(QTabWidget.TabPosition.South) 73 self.channelsWidget.setTabPosition(QTabWidget.TabPosition.South)
75 74
76 height = self.height() 75 height = self.height()
77 self.splitter.setSizes([int(height * 0.6), int(height * 0.4)]) 76 self.splitter.setSizes([int(height * 0.6), int(height * 0.4)])
78 77
79 self.__channelList = [] 78 self.__channelList = []
80 self.__channelTypePrefixes = "" 79 self.__channelTypePrefixes = ""
81 self.__userName = "" 80 self.__userName = ""
82 self.__identityName = "" 81 self.__identityName = ""
83 self.__quitMessage = "" 82 self.__quitMessage = ""
84 self.__nickIndex = -1 83 self.__nickIndex = -1
85 self.__nickName = "" 84 self.__nickName = ""
86 self.__server = None 85 self.__server = None
87 self.__registering = False 86 self.__registering = False
88 87
89 self.__connectionState = IrcWidget.ServerDisconnected 88 self.__connectionState = IrcWidget.ServerDisconnected
90 self.__sslErrorLock = False 89 self.__sslErrorLock = False
91 90
92 self.__buffer = "" 91 self.__buffer = ""
93 self.__userPrefix = {} 92 self.__userPrefix = {}
94 93
95 self.__socket = None 94 self.__socket = None
96 if SSL_AVAILABLE: 95 if SSL_AVAILABLE:
97 self.__sslErrorHandler = EricSslErrorHandler(self) 96 self.__sslErrorHandler = EricSslErrorHandler(self)
98 else: 97 else:
99 self.__sslErrorHandler = None 98 self.__sslErrorHandler = None
100 99
101 self.__patterns = [ 100 self.__patterns = [
102 # :foo_!n=foo@foohost.bar.net PRIVMSG bar_ :some long message 101 # :foo_!n=foo@foohost.bar.net PRIVMSG bar_ :some long message
103 (re.compile(r":([^!]+)!([^ ]+)\sPRIVMSG\s([^ ]+)\s:(.*)"), 102 (re.compile(r":([^!]+)!([^ ]+)\sPRIVMSG\s([^ ]+)\s:(.*)"), self.__query),
104 self.__query),
105 # :foo.bar.net COMMAND some message 103 # :foo.bar.net COMMAND some message
106 (re.compile(r""":([^ ]+)\s+([A-Z]+)\s+(.+)"""), 104 (re.compile(r""":([^ ]+)\s+([A-Z]+)\s+(.+)"""), self.__handleNamedMessage),
107 self.__handleNamedMessage),
108 # :foo.bar.net 123 * :info 105 # :foo.bar.net 123 * :info
109 (re.compile(r""":([^ ]+)\s+(\d{3})\s+(.+)"""), 106 (re.compile(r""":([^ ]+)\s+(\d{3})\s+(.+)"""), self.__handleNumericMessage),
110 self.__handleNumericMessage),
111 # PING :ping message 107 # PING :ping message
112 (re.compile(r"""PING\s+:(.*)"""), self.__ping), 108 (re.compile(r"""PING\s+:(.*)"""), self.__ping),
113 ] 109 ]
114 self.__prefixRe = re.compile(r""".*\sPREFIX=\((.*)\)([^ ]+).*""") 110 self.__prefixRe = re.compile(r""".*\sPREFIX=\((.*)\)([^ ]+).*""")
115 self.__chanTypesRe = re.compile(r""".*\sCHANTYPES=([^ ]+).*""") 111 self.__chanTypesRe = re.compile(r""".*\sCHANTYPES=([^ ]+).*""")
116 112
117 ircPic = UI.PixmapCache.getPixmap("irc128") 113 ircPic = UI.PixmapCache.getPixmap("irc128")
118 self.__emptyLabel = QLabel() 114 self.__emptyLabel = QLabel()
119 self.__emptyLabel.setPixmap(ircPic) 115 self.__emptyLabel.setPixmap(ircPic)
120 self.__emptyLabel.setAlignment( 116 self.__emptyLabel.setAlignment(
121 Qt.AlignmentFlag.AlignVCenter | Qt.AlignmentFlag.AlignHCenter) 117 Qt.AlignmentFlag.AlignVCenter | Qt.AlignmentFlag.AlignHCenter
118 )
122 self.channelsWidget.addTab(self.__emptyLabel, "") 119 self.channelsWidget.addTab(self.__emptyLabel, "")
123 120
124 # all initialized, do connections now 121 # all initialized, do connections now
125 self.__ircNetworkManager.dataChanged.connect(self.__networkDataChanged) 122 self.__ircNetworkManager.dataChanged.connect(self.__networkDataChanged)
126 self.networkWidget.initialize(self.__ircNetworkManager) 123 self.networkWidget.initialize(self.__ircNetworkManager)
127 self.networkWidget.connectNetwork.connect(self.__connectNetwork) 124 self.networkWidget.connectNetwork.connect(self.__connectNetwork)
128 self.networkWidget.editNetwork.connect(self.__editNetwork) 125 self.networkWidget.editNetwork.connect(self.__editNetwork)
129 self.networkWidget.joinChannel.connect(self.joinChannel) 126 self.networkWidget.joinChannel.connect(self.joinChannel)
130 self.networkWidget.nickChanged.connect(self.__changeNick) 127 self.networkWidget.nickChanged.connect(self.__changeNick)
131 self.networkWidget.sendData.connect(self.__send) 128 self.networkWidget.sendData.connect(self.__send)
132 self.networkWidget.away.connect(self.__away) 129 self.networkWidget.away.connect(self.__away)
133 self.networkWidget.autoConnected.connect(self.autoConnected) 130 self.networkWidget.autoConnected.connect(self.autoConnected)
134 131
135 def shutdown(self): 132 def shutdown(self):
136 """ 133 """
137 Public method to shut down the widget. 134 Public method to shut down the widget.
138 135
139 @return flag indicating successful shutdown (boolean) 136 @return flag indicating successful shutdown (boolean)
140 """ 137 """
141 if self.__server: 138 if self.__server:
142 if Preferences.getIrc("AskOnShutdown"): 139 if Preferences.getIrc("AskOnShutdown"):
143 ok = EricMessageBox.yesNo( 140 ok = EricMessageBox.yesNo(
144 self, 141 self,
145 self.tr("Disconnect from Server"), 142 self.tr("Disconnect from Server"),
146 self.tr( 143 self.tr(
147 """<p>Do you really want to disconnect from""" 144 """<p>Do you really want to disconnect from"""
148 """ <b>{0}</b>?</p><p>All channels will be closed.""" 145 """ <b>{0}</b>?</p><p>All channels will be closed."""
149 """</p>""").format(self.__server.getName())) 146 """</p>"""
147 ).format(self.__server.getName()),
148 )
150 else: 149 else:
151 ok = True 150 ok = True
152 if ok: 151 if ok:
153 self.__connectNetwork("", False, True) 152 self.__connectNetwork("", False, True)
154 else: 153 else:
155 ok = True 154 ok = True
156 155
157 if ok: 156 if ok:
158 self.__ircNetworkManager.close() 157 self.__ircNetworkManager.close()
159 158
160 return ok 159 return ok
161 160
162 def autoConnect(self): 161 def autoConnect(self):
163 """ 162 """
164 Public method to initiate the IRC auto connection. 163 Public method to initiate the IRC auto connection.
165 """ 164 """
166 self.networkWidget.autoConnect() 165 self.networkWidget.autoConnect()
167 166
168 def __connectNetwork(self, name, connect, silent): 167 def __connectNetwork(self, name, connect, silent):
169 """ 168 """
170 Private slot to connect to or disconnect from the given network. 169 Private slot to connect to or disconnect from the given network.
171 170
172 @param name name of the network to connect to (string) 171 @param name name of the network to connect to (string)
173 @param connect flag indicating to connect (boolean) 172 @param connect flag indicating to connect (boolean)
174 @param silent flag indicating a silent connect/disconnect (boolean) 173 @param silent flag indicating a silent connect/disconnect (boolean)
175 """ 174 """
176 if connect: 175 if connect:
177 network = self.__ircNetworkManager.getNetwork(name) 176 network = self.__ircNetworkManager.getNetwork(name)
178 if network: 177 if network:
179 self.__server = network.getServer() 178 self.__server = network.getServer()
180 self.__identityName = network.getIdentityName() 179 self.__identityName = network.getIdentityName()
181 identity = self.__ircNetworkManager.getIdentity( 180 identity = self.__ircNetworkManager.getIdentity(self.__identityName)
182 self.__identityName)
183 if identity: 181 if identity:
184 self.__userName = identity.getIdent() 182 self.__userName = identity.getIdent()
185 self.__quitMessage = identity.getQuitMessage() 183 self.__quitMessage = identity.getQuitMessage()
186 if self.__server: 184 if self.__server:
187 useSSL = self.__server.useSSL() 185 useSSL = self.__server.useSSL()
191 self.tr("SSL Connection"), 189 self.tr("SSL Connection"),
192 self.tr( 190 self.tr(
193 """An encrypted connection to the IRC""" 191 """An encrypted connection to the IRC"""
194 """ network was requested but SSL is not""" 192 """ network was requested but SSL is not"""
195 """ available. Please change the server""" 193 """ available. Please change the server"""
196 """ configuration.""")) 194 """ configuration."""
195 ),
196 )
197 return 197 return
198 198
199 if useSSL: 199 if useSSL:
200 # create SSL socket 200 # create SSL socket
201 self.__socket = QSslSocket(self) 201 self.__socket = QSslSocket(self)
202 self.__socket.encrypted.connect( 202 self.__socket.encrypted.connect(self.__hostConnected)
203 self.__hostConnected) 203 self.__socket.sslErrors.connect(self.__sslErrors)
204 self.__socket.sslErrors.connect(
205 self.__sslErrors)
206 else: 204 else:
207 # create TCP socket 205 # create TCP socket
208 self.__socket = QTcpSocket(self) 206 self.__socket = QTcpSocket(self)
209 self.__socket.connected.connect( 207 self.__socket.connected.connect(self.__hostConnected)
210 self.__hostConnected) 208 self.__socket.hostFound.connect(self.__hostFound)
211 self.__socket.hostFound.connect( 209 self.__socket.disconnected.connect(self.__hostDisconnected)
212 self.__hostFound) 210 self.__socket.readyRead.connect(self.__readyRead)
213 self.__socket.disconnected.connect( 211 self.__socket.errorOccurred.connect(self.__tcpError)
214 self.__hostDisconnected) 212
215 self.__socket.readyRead.connect(
216 self.__readyRead)
217 self.__socket.errorOccurred.connect(
218 self.__tcpError)
219
220 self.__connectionState = IrcWidget.ServerConnecting 213 self.__connectionState = IrcWidget.ServerConnecting
221 if useSSL: 214 if useSSL:
222 self.networkWidget.addServerMessage( 215 self.networkWidget.addServerMessage(
223 self.tr("Info"), 216 self.tr("Info"),
224 self.tr("Looking for server {0} (port {1})" 217 self.tr(
225 " using an SSL encrypted connection" 218 "Looking for server {0} (port {1})"
226 "...").format(self.__server.getName(), 219 " using an SSL encrypted connection"
227 self.__server.getPort())) 220 "..."
221 ).format(
222 self.__server.getName(), self.__server.getPort()
223 ),
224 )
228 self.__socket.connectToHostEncrypted( 225 self.__socket.connectToHostEncrypted(
229 self.__server.getName(), 226 self.__server.getName(), self.__server.getPort()
230 self.__server.getPort()
231 ) 227 )
232 else: 228 else:
233 self.networkWidget.addServerMessage( 229 self.networkWidget.addServerMessage(
234 self.tr("Info"), 230 self.tr("Info"),
235 self.tr( 231 self.tr("Looking for server {0} (port {1})...").format(
236 "Looking for server {0} (port {1})...") 232 self.__server.getName(), self.__server.getPort()
237 .format( 233 ),
238 self.__server.getName(), 234 )
239 self.__server.getPort()))
240 self.__socket.connectToHost( 235 self.__socket.connectToHost(
241 self.__server.getName(), 236 self.__server.getName(), self.__server.getPort()
242 self.__server.getPort()) 237 )
243 else: 238 else:
244 if silent: 239 if silent:
245 ok = True 240 ok = True
246 else: 241 else:
247 ok = EricMessageBox.yesNo( 242 ok = EricMessageBox.yesNo(
248 self, 243 self,
249 self.tr("Disconnect from Server"), 244 self.tr("Disconnect from Server"),
250 self.tr("""<p>Do you really want to disconnect from""" 245 self.tr(
251 """ <b>{0}</b>?</p><p>All channels will be""" 246 """<p>Do you really want to disconnect from"""
252 """ closed.</p>""") 247 """ <b>{0}</b>?</p><p>All channels will be"""
253 .format(self.__server.getName())) 248 """ closed.</p>"""
249 ).format(self.__server.getName()),
250 )
254 if ok: 251 if ok:
255 if self.__server is not None: 252 if self.__server is not None:
256 self.networkWidget.addServerMessage( 253 self.networkWidget.addServerMessage(
257 self.tr("Info"), 254 self.tr("Info"),
258 self.tr("Disconnecting from server {0}...").format( 255 self.tr("Disconnecting from server {0}...").format(
259 self.__server.getName())) 256 self.__server.getName()
257 ),
258 )
260 elif name: 259 elif name:
261 self.networkWidget.addServerMessage( 260 self.networkWidget.addServerMessage(
262 self.tr("Info"), 261 self.tr("Info"),
263 self.tr("Disconnecting from network {0}...").format( 262 self.tr("Disconnecting from network {0}...").format(name),
264 name)) 263 )
265 else: 264 else:
266 self.networkWidget.addServerMessage( 265 self.networkWidget.addServerMessage(
267 self.tr("Info"), 266 self.tr("Info"), self.tr("Disconnecting from server.")
268 self.tr("Disconnecting from server.")) 267 )
269 self.__closeAllChannels() 268 self.__closeAllChannels()
270 self.__send("QUIT :" + self.__quitMessage) 269 self.__send("QUIT :" + self.__quitMessage)
271 if self.__socket: 270 if self.__socket:
272 self.__socket.flush() 271 self.__socket.flush()
273 self.__socket.close() 272 self.__socket.close()
276 self.__socket.deleteLater() 275 self.__socket.deleteLater()
277 self.__socket = None 276 self.__socket = None
278 self.__userName = "" 277 self.__userName = ""
279 self.__identityName = "" 278 self.__identityName = ""
280 self.__quitMessage = "" 279 self.__quitMessage = ""
281 280
282 def __editNetwork(self, name): 281 def __editNetwork(self, name):
283 """ 282 """
284 Private slot to edit the network configuration. 283 Private slot to edit the network configuration.
285 284
286 @param name name of the network to edit (string) 285 @param name name of the network to edit (string)
287 """ 286 """
288 from .IrcNetworkListDialog import IrcNetworkListDialog 287 from .IrcNetworkListDialog import IrcNetworkListDialog
288
289 dlg = IrcNetworkListDialog(self.__ircNetworkManager, self) 289 dlg = IrcNetworkListDialog(self.__ircNetworkManager, self)
290 dlg.exec() 290 dlg.exec()
291 291
292 def __networkDataChanged(self): 292 def __networkDataChanged(self):
293 """ 293 """
294 Private slot handling changes of the network and identity definitions. 294 Private slot handling changes of the network and identity definitions.
295 """ 295 """
296 identity = self.__ircNetworkManager.getIdentity(self.__identityName) 296 identity = self.__ircNetworkManager.getIdentity(self.__identityName)
297 if identity: 297 if identity:
298 partMsg = identity.getPartMessage() 298 partMsg = identity.getPartMessage()
299 for channel in self.__channelList: 299 for channel in self.__channelList:
300 channel.setPartMessage(partMsg) 300 channel.setPartMessage(partMsg)
301 301
302 def joinChannel(self, name, key=""): 302 def joinChannel(self, name, key=""):
303 """ 303 """
304 Public slot to join a channel. 304 Public slot to join a channel.
305 305
306 @param name name of the channel (string) 306 @param name name of the channel (string)
307 @param key key of the channel (string) 307 @param key key of the channel (string)
308 """ 308 """
309 # step 1: check, if this channel is already joined 309 # step 1: check, if this channel is already joined
310 for channel in self.__channelList: 310 for channel in self.__channelList:
311 if channel.name() == name: 311 if channel.name() == name:
312 return 312 return
313 313
314 from .IrcChannelWidget import IrcChannelWidget 314 from .IrcChannelWidget import IrcChannelWidget
315
315 channel = IrcChannelWidget(self) 316 channel = IrcChannelWidget(self)
316 channel.setName(name) 317 channel.setName(name)
317 channel.setUserName(self.__nickName) 318 channel.setUserName(self.__nickName)
318 identity = self.__ircNetworkManager.getIdentity(self.__identityName) 319 identity = self.__ircNetworkManager.getIdentity(self.__identityName)
319 if identity: 320 if identity:
320 channel.setPartMessage(identity.getPartMessage()) 321 channel.setPartMessage(identity.getPartMessage())
321 channel.setUserPrivilegePrefix(self.__userPrefix) 322 channel.setUserPrivilegePrefix(self.__userPrefix)
322 channel.initAutoWho() 323 channel.initAutoWho()
323 324
324 channel.sendData.connect(self.__send) 325 channel.sendData.connect(self.__send)
325 channel.sendCtcpRequest.connect(self.__sendCtcpRequest) 326 channel.sendCtcpRequest.connect(self.__sendCtcpRequest)
326 channel.sendCtcpReply.connect(self.__sendCtcpReply) 327 channel.sendCtcpReply.connect(self.__sendCtcpReply)
327 channel.channelClosed.connect(self.__closeChannel) 328 channel.channelClosed.connect(self.__closeChannel)
328 channel.openPrivateChat.connect(self.__openPrivate) 329 channel.openPrivateChat.connect(self.__openPrivate)
329 channel.awayCommand.connect(self.networkWidget.handleAwayCommand) 330 channel.awayCommand.connect(self.networkWidget.handleAwayCommand)
330 channel.leaveChannels.connect(self.__leaveChannels) 331 channel.leaveChannels.connect(self.__leaveChannels)
331 channel.leaveAllChannels.connect(self.__leaveAllChannels) 332 channel.leaveAllChannels.connect(self.__leaveAllChannels)
332 333
333 self.channelsWidget.addTab(channel, name) 334 self.channelsWidget.addTab(channel, name)
334 self.__channelList.append(channel) 335 self.__channelList.append(channel)
335 self.channelsWidget.setCurrentWidget(channel) 336 self.channelsWidget.setCurrentWidget(channel)
336 337
337 joinCommand = ["JOIN", name] 338 joinCommand = ["JOIN", name]
338 if key: 339 if key:
339 joinCommand.append(key) 340 joinCommand.append(key)
340 self.__send(" ".join(joinCommand)) 341 self.__send(" ".join(joinCommand))
341 self.__send("MODE " + name) 342 self.__send("MODE " + name)
342 343
343 emptyIndex = self.channelsWidget.indexOf(self.__emptyLabel) 344 emptyIndex = self.channelsWidget.indexOf(self.__emptyLabel)
344 if emptyIndex > -1: 345 if emptyIndex > -1:
345 self.channelsWidget.removeTab(emptyIndex) 346 self.channelsWidget.removeTab(emptyIndex)
346 self.__leaveButton.setEnabled(True) 347 self.__leaveButton.setEnabled(True)
347 self.channelsWidget.setTabsClosable(True) 348 self.channelsWidget.setTabsClosable(True)
348 349
349 def __query(self, match): 350 def __query(self, match):
350 """ 351 """
351 Private method to handle a new private connection. 352 Private method to handle a new private connection.
352 353
353 @param match reference to the match object 354 @param match reference to the match object
354 @return flag indicating, if the message was handled (boolean) 355 @return flag indicating, if the message was handled (boolean)
355 """ 356 """
356 # group(1) sender user name 357 # group(1) sender user name
357 # group(2) sender user@host 358 # group(2) sender user@host
358 # group(3) target nick 359 # group(3) target nick
359 # group(4) message 360 # group(4) message
360 if match.group(4).startswith("\x01"): 361 if match.group(4).startswith("\x01"):
361 return self.__handleCtcp(match) 362 return self.__handleCtcp(match)
362 363
363 self.__openPrivate(match.group(1)) 364 self.__openPrivate(match.group(1))
364 # the above call sets the new channel as the current widget 365 # the above call sets the new channel as the current widget
365 channel = self.channelsWidget.currentWidget() 366 channel = self.channelsWidget.currentWidget()
366 channel.addMessage(match.group(1), match.group(4)) 367 channel.addMessage(match.group(1), match.group(4))
367 channel.setPrivateInfo( 368 channel.setPrivateInfo("{0} - {1}".format(match.group(1), match.group(2)))
368 "{0} - {1}".format(match.group(1), match.group(2))) 369
369
370 return True 370 return True
371 371
372 @pyqtSlot(str) 372 @pyqtSlot(str)
373 def __openPrivate(self, name): 373 def __openPrivate(self, name):
374 """ 374 """
375 Private slot to open a private chat with the given user. 375 Private slot to open a private chat with the given user.
376 376
377 @param name name of the user (string) 377 @param name name of the user (string)
378 """ 378 """
379 from .IrcChannelWidget import IrcChannelWidget 379 from .IrcChannelWidget import IrcChannelWidget
380
380 channel = IrcChannelWidget(self) 381 channel = IrcChannelWidget(self)
381 channel.setName(self.__nickName) 382 channel.setName(self.__nickName)
382 channel.setUserName(self.__nickName) 383 channel.setUserName(self.__nickName)
383 identity = self.__ircNetworkManager.getIdentity(self.__identityName) 384 identity = self.__ircNetworkManager.getIdentity(self.__identityName)
384 if identity: 385 if identity:
385 channel.setPartMessage(identity.getPartMessage()) 386 channel.setPartMessage(identity.getPartMessage())
386 channel.setUserPrivilegePrefix(self.__userPrefix) 387 channel.setUserPrivilegePrefix(self.__userPrefix)
387 channel.setPrivate(True, name) 388 channel.setPrivate(True, name)
388 channel.addUsers([name, self.__nickName]) 389 channel.addUsers([name, self.__nickName])
389 390
390 channel.sendData.connect(self.__send) 391 channel.sendData.connect(self.__send)
391 channel.sendCtcpRequest.connect(self.__sendCtcpRequest) 392 channel.sendCtcpRequest.connect(self.__sendCtcpRequest)
392 channel.sendCtcpReply.connect(self.__sendCtcpReply) 393 channel.sendCtcpReply.connect(self.__sendCtcpReply)
393 channel.channelClosed.connect(self.__closeChannel) 394 channel.channelClosed.connect(self.__closeChannel)
394 channel.awayCommand.connect(self.networkWidget.handleAwayCommand) 395 channel.awayCommand.connect(self.networkWidget.handleAwayCommand)
395 channel.leaveChannels.connect(self.__leaveChannels) 396 channel.leaveChannels.connect(self.__leaveChannels)
396 channel.leaveAllChannels.connect(self.__leaveAllChannels) 397 channel.leaveAllChannels.connect(self.__leaveAllChannels)
397 398
398 self.channelsWidget.addTab(channel, name) 399 self.channelsWidget.addTab(channel, name)
399 self.__channelList.append(channel) 400 self.__channelList.append(channel)
400 self.channelsWidget.setCurrentWidget(channel) 401 self.channelsWidget.setCurrentWidget(channel)
401 402
402 @pyqtSlot() 403 @pyqtSlot()
403 def __leaveChannel(self): 404 def __leaveChannel(self):
404 """ 405 """
405 Private slot to leave a channel and close the associated tab. 406 Private slot to leave a channel and close the associated tab.
406 """ 407 """
407 channel = self.channelsWidget.currentWidget() 408 channel = self.channelsWidget.currentWidget()
408 channel.requestLeave() 409 channel.requestLeave()
409 410
410 @pyqtSlot(list) 411 @pyqtSlot(list)
411 def __leaveChannels(self, channelNames): 412 def __leaveChannels(self, channelNames):
412 """ 413 """
413 Private slot to leave a list of channels and close their associated 414 Private slot to leave a list of channels and close their associated
414 tabs. 415 tabs.
415 416
416 @param channelNames list of channels to leave 417 @param channelNames list of channels to leave
417 @type list of str 418 @type list of str
418 """ 419 """
419 for channelName in channelNames: 420 for channelName in channelNames:
420 for channel in self.__channelList: 421 for channel in self.__channelList:
421 if channel.name() == channelName: 422 if channel.name() == channelName:
422 channel.leaveChannel() 423 channel.leaveChannel()
423 424
424 @pyqtSlot() 425 @pyqtSlot()
425 def __leaveAllChannels(self): 426 def __leaveAllChannels(self):
426 """ 427 """
427 Private slot to leave all channels and close their tabs. 428 Private slot to leave all channels and close their tabs.
428 """ 429 """
429 while self.__channelList: 430 while self.__channelList:
430 channel = self.__channelList[0] 431 channel = self.__channelList[0]
431 channel.leaveChannel() 432 channel.leaveChannel()
432 433
433 def __closeAllChannels(self): 434 def __closeAllChannels(self):
434 """ 435 """
435 Private method to close all channels. 436 Private method to close all channels.
436 """ 437 """
437 while self.__channelList: 438 while self.__channelList:
438 channel = self.__channelList.pop() 439 channel = self.__channelList.pop()
439 self.channelsWidget.removeTab(self.channelsWidget.indexOf(channel)) 440 self.channelsWidget.removeTab(self.channelsWidget.indexOf(channel))
440 channel.deleteLater() 441 channel.deleteLater()
441 channel = None 442 channel = None
442 443
443 self.channelsWidget.addTab(self.__emptyLabel, "") 444 self.channelsWidget.addTab(self.__emptyLabel, "")
444 self.__emptyLabel.show() 445 self.__emptyLabel.show()
445 self.__leaveButton.setEnabled(False) 446 self.__leaveButton.setEnabled(False)
446 self.channelsWidget.setTabsClosable(False) 447 self.channelsWidget.setTabsClosable(False)
447 448
448 def __closeChannel(self, name): 449 def __closeChannel(self, name):
449 """ 450 """
450 Private slot handling the closing of a channel. 451 Private slot handling the closing of a channel.
451 452
452 @param name name of the closed channel (string) 453 @param name name of the closed channel (string)
453 """ 454 """
454 for channel in self.__channelList: 455 for channel in self.__channelList:
455 if channel.name() == name: 456 if channel.name() == name:
456 self.channelsWidget.removeTab( 457 self.channelsWidget.removeTab(self.channelsWidget.indexOf(channel))
457 self.channelsWidget.indexOf(channel))
458 self.__channelList.remove(channel) 458 self.__channelList.remove(channel)
459 channel.deleteLater() 459 channel.deleteLater()
460 460
461 if self.channelsWidget.count() == 0: 461 if self.channelsWidget.count() == 0:
462 self.channelsWidget.addTab(self.__emptyLabel, "") 462 self.channelsWidget.addTab(self.__emptyLabel, "")
463 self.__emptyLabel.show() 463 self.__emptyLabel.show()
464 self.__leaveButton.setEnabled(False) 464 self.__leaveButton.setEnabled(False)
465 self.channelsWidget.setTabsClosable(False) 465 self.channelsWidget.setTabsClosable(False)
466 466
467 @pyqtSlot(int) 467 @pyqtSlot(int)
468 def on_channelsWidget_tabCloseRequested(self, index): 468 def on_channelsWidget_tabCloseRequested(self, index):
469 """ 469 """
470 Private slot to close a channel by pressing the close button of 470 Private slot to close a channel by pressing the close button of
471 the channels widget. 471 the channels widget.
472 472
473 @param index index of the tab to be closed (integer) 473 @param index index of the tab to be closed (integer)
474 """ 474 """
475 channel = self.channelsWidget.widget(index) 475 channel = self.channelsWidget.widget(index)
476 channel.requestLeave() 476 channel.requestLeave()
477 477
478 def __send(self, data): 478 def __send(self, data):
479 """ 479 """
480 Private slot to send data to the IRC server. 480 Private slot to send data to the IRC server.
481 481
482 @param data data to be sent (string) 482 @param data data to be sent (string)
483 """ 483 """
484 if self.__socket: 484 if self.__socket:
485 self.__socket.write( 485 self.__socket.write(QByteArray("{0}\r\n".format(data).encode("utf-8")))
486 QByteArray("{0}\r\n".format(data).encode("utf-8"))) 486
487
488 def __sendCtcpRequest(self, receiver, request, arguments): 487 def __sendCtcpRequest(self, receiver, request, arguments):
489 """ 488 """
490 Private slot to send a CTCP request. 489 Private slot to send a CTCP request.
491 490
492 @param receiver nick name of the receiver 491 @param receiver nick name of the receiver
493 @type str 492 @type str
494 @param request CTCP request to be sent 493 @param request CTCP request to be sent
495 @type str 494 @type str
496 @param arguments arguments to be sent 495 @param arguments arguments to be sent
497 @type str 496 @type str
498 """ 497 """
499 request = request.upper() 498 request = request.upper()
500 if request == "PING": 499 if request == "PING":
501 arguments = "Eric IRC {0}".format( 500 arguments = "Eric IRC {0}".format(QDateTime.currentMSecsSinceEpoch())
502 QDateTime.currentMSecsSinceEpoch()) 501
503 502 self.__send("PRIVMSG {0} :\x01{1} {2}\x01".format(receiver, request, arguments))
504 self.__send("PRIVMSG {0} :\x01{1} {2}\x01".format( 503
505 receiver, request, arguments))
506
507 def __sendCtcpReply(self, receiver, text): 504 def __sendCtcpReply(self, receiver, text):
508 """ 505 """
509 Private slot to send a CTCP reply. 506 Private slot to send a CTCP reply.
510 507
511 @param receiver nick name of the receiver 508 @param receiver nick name of the receiver
512 @type str 509 @type str
513 @param text text to be sent 510 @param text text to be sent
514 @type str 511 @type str
515 """ 512 """
516 self.__send("NOTICE {0} :\x01{1}\x01".format(receiver, text)) 513 self.__send("NOTICE {0} :\x01{1}\x01".format(receiver, text))
517 514
518 def __hostFound(self): 515 def __hostFound(self):
519 """ 516 """
520 Private slot to indicate the host was found. 517 Private slot to indicate the host was found.
521 """ 518 """
522 self.networkWidget.addServerMessage( 519 self.networkWidget.addServerMessage(
523 self.tr("Info"), 520 self.tr("Info"), self.tr("Server found,connecting...")
524 self.tr("Server found,connecting...")) 521 )
525 522
526 def __hostConnected(self): 523 def __hostConnected(self):
527 """ 524 """
528 Private slot to log in to the server after the connection was 525 Private slot to log in to the server after the connection was
529 established. 526 established.
530 """ 527 """
531 self.networkWidget.addServerMessage( 528 self.networkWidget.addServerMessage(
532 self.tr("Info"), 529 self.tr("Info"), self.tr("Connected,logging in...")
533 self.tr("Connected,logging in...")) 530 )
534 self.networkWidget.setConnected(True) 531 self.networkWidget.setConnected(True)
535 532
536 self.__registering = True 533 self.__registering = True
537 serverPassword = self.__server.getPassword() 534 serverPassword = self.__server.getPassword()
538 if serverPassword: 535 if serverPassword:
539 self.__send("PASS " + serverPassword) 536 self.__send("PASS " + serverPassword)
540 537
541 identity = self.__ircNetworkManager.getIdentity( 538 identity = self.__ircNetworkManager.getIdentity(self.__identityName)
542 self.__identityName)
543 nick = self.networkWidget.getNickname() 539 nick = self.networkWidget.getNickname()
544 if not nick and identity: 540 if not nick and identity:
545 self.__nickIndex = 0 541 self.__nickIndex = 0
546 try: 542 try:
547 nick = identity.getNickNames()[self.__nickIndex] 543 nick = identity.getNickNames()[self.__nickIndex]
555 realName = identity.getRealName() 551 realName = identity.getRealName()
556 if not realName: 552 if not realName:
557 realName = "eric IDE chat" 553 realName = "eric IDE chat"
558 self.__send("NICK " + nick) 554 self.__send("NICK " + nick)
559 self.__send("USER " + self.__userName + " 0 * :" + realName) 555 self.__send("USER " + self.__userName + " 0 * :" + realName)
560 556
561 def __hostDisconnected(self): 557 def __hostDisconnected(self):
562 """ 558 """
563 Private slot to indicate the host was disconnected. 559 Private slot to indicate the host was disconnected.
564 """ 560 """
565 if self.networkWidget.isConnected(): 561 if self.networkWidget.isConnected():
566 self.__closeAllChannels() 562 self.__closeAllChannels()
567 self.networkWidget.addServerMessage( 563 self.networkWidget.addServerMessage(
568 self.tr("Info"), 564 self.tr("Info"), self.tr("Server disconnected.")
569 self.tr("Server disconnected.")) 565 )
570 self.networkWidget.setRegistered(False) 566 self.networkWidget.setRegistered(False)
571 self.networkWidget.setConnected(False) 567 self.networkWidget.setConnected(False)
572 self.__server = None 568 self.__server = None
573 self.__nickName = "" 569 self.__nickName = ""
574 self.__nickIndex = -1 570 self.__nickIndex = -1
575 self.__channelTypePrefixes = "" 571 self.__channelTypePrefixes = ""
576 572
577 if self.__socket: 573 if self.__socket:
578 self.__socket.deleteLater() 574 self.__socket.deleteLater()
579 self.__socket = None 575 self.__socket = None
580 576
581 self.__connectionState = IrcWidget.ServerDisconnected 577 self.__connectionState = IrcWidget.ServerDisconnected
582 self.__sslErrorLock = False 578 self.__sslErrorLock = False
583 579
584 def __readyRead(self): 580 def __readyRead(self):
585 """ 581 """
586 Private slot to read data from the socket. 582 Private slot to read data from the socket.
587 """ 583 """
588 if self.__socket: 584 if self.__socket:
589 self.__buffer += str( 585 self.__buffer += str(
590 self.__socket.readAll(), 586 self.__socket.readAll(), Preferences.getSystem("IOEncoding"), "replace"
591 Preferences.getSystem("IOEncoding"), 587 )
592 'replace')
593 if self.__buffer.endswith("\r\n"): 588 if self.__buffer.endswith("\r\n"):
594 for line in self.__buffer.splitlines(): 589 for line in self.__buffer.splitlines():
595 line = line.strip() 590 line = line.strip()
596 if line: 591 if line:
597 logging.debug("<IRC> %s", line) 592 logging.debug("<IRC> %s", line)
611 # Oops, the message wasn't handled 606 # Oops, the message wasn't handled
612 self.networkWidget.addErrorMessage( 607 self.networkWidget.addErrorMessage(
613 self.tr("Message Error"), 608 self.tr("Message Error"),
614 self.tr( 609 self.tr(
615 "Unknown message received from server:" 610 "Unknown message received from server:"
616 "<br/>{0}").format(line)) 611 "<br/>{0}"
617 612 ).format(line),
613 )
614
618 self.__updateUsersCount() 615 self.__updateUsersCount()
619 self.__buffer = "" 616 self.__buffer = ""
620 617
621 def __handleCtcpReply(self, match): 618 def __handleCtcpReply(self, match):
622 """ 619 """
623 Private method to handle a server message containing a CTCP reply. 620 Private method to handle a server message containing a CTCP reply.
624 621
625 @param match reference to the match object 622 @param match reference to the match object
626 """ 623 """
627 if "!" in match.group(1): 624 if "!" in match.group(1):
628 sender = match.group(1).split("!", 1)[0] 625 sender = match.group(1).split("!", 1)[0]
629 626
630 try: 627 try:
631 ctcpCommand = match.group(3).split(":", 1)[1] 628 ctcpCommand = match.group(3).split(":", 1)[1]
632 except IndexError: 629 except IndexError:
633 ctcpCommand = match.group(3) 630 ctcpCommand = match.group(3)
634 ctcpCommand = ctcpCommand[1:].split("\x01", 1)[0] 631 ctcpCommand = ctcpCommand[1:].split("\x01", 1)[0]
635 if " " in ctcpCommand: 632 if " " in ctcpCommand:
636 ctcpReply, ctcpArg = ctcpCommand.split(" ", 1) 633 ctcpReply, ctcpArg = ctcpCommand.split(" ", 1)
637 else: 634 else:
638 ctcpReply, ctcpArg = ctcpCommand, "" 635 ctcpReply, ctcpArg = ctcpCommand, ""
639 ctcpReply = ctcpReply.upper() 636 ctcpReply = ctcpReply.upper()
640 637
641 if ctcpReply == "PING" and ctcpArg.startswith("Eric IRC "): 638 if ctcpReply == "PING" and ctcpArg.startswith("Eric IRC "):
642 # it is a response to a ping request 639 # it is a response to a ping request
643 pingDateTime = int(ctcpArg.split()[-1]) 640 pingDateTime = int(ctcpArg.split()[-1])
644 latency = QDateTime.currentMSecsSinceEpoch() - pingDateTime 641 latency = QDateTime.currentMSecsSinceEpoch() - pingDateTime
645 self.networkWidget.addServerMessage( 642 self.networkWidget.addServerMessage(
646 self.tr("CTCP"), 643 self.tr("CTCP"),
647 self.tr( 644 self.tr(
648 "Received CTCP-PING response from {0} with latency" 645 "Received CTCP-PING response from {0} with latency"
649 " of {1} ms.").format(sender, latency)) 646 " of {1} ms."
647 ).format(sender, latency),
648 )
650 else: 649 else:
651 self.networkWidget.addServerMessage( 650 self.networkWidget.addServerMessage(
652 self.tr("CTCP"), 651 self.tr("CTCP"),
653 self.tr( 652 self.tr("Received unknown CTCP-{0} response from {1}.").format(
654 "Received unknown CTCP-{0} response from {1}.") 653 ctcpReply, sender
655 .format(ctcpReply, sender)) 654 ),
656 655 )
656
657 def __handleNamedMessage(self, match): 657 def __handleNamedMessage(self, match):
658 """ 658 """
659 Private method to handle a server message containing a message name. 659 Private method to handle a server message containing a message name.
660 660
661 @param match reference to the match object 661 @param match reference to the match object
662 @return flag indicating, if the message was handled (boolean) 662 @return flag indicating, if the message was handled (boolean)
663 """ 663 """
664 name = match.group(2) 664 name = match.group(2)
665 if name == "NOTICE": 665 if name == "NOTICE":
666 try: 666 try:
667 msg = match.group(3).split(":", 1)[1] 667 msg = match.group(3).split(":", 1)[1]
668 except IndexError: 668 except IndexError:
669 msg = match.group(3) 669 msg = match.group(3)
670 670
671 if msg.startswith("\x01"): 671 if msg.startswith("\x01"):
672 self.__handleCtcpReply(match) 672 self.__handleCtcpReply(match)
673 return True 673 return True
674 674
675 if "!" in match.group(1): 675 if "!" in match.group(1):
676 name = match.group(1).split("!", 1)[0] 676 name = match.group(1).split("!", 1)[0]
677 msg = "-{0}- {1}".format(name, msg) 677 msg = "-{0}- {1}".format(name, msg)
678 self.networkWidget.addServerMessage(self.tr("Notice"), msg) 678 self.networkWidget.addServerMessage(self.tr("Notice"), msg)
679 return True 679 return True
681 self.__registering = False 681 self.__registering = False
682 if ":" in match.group(3): 682 if ":" in match.group(3):
683 # :foo MODE foo :+i 683 # :foo MODE foo :+i
684 name, modes = match.group(3).split(" :") 684 name, modes = match.group(3).split(" :")
685 sourceNick = match.group(1) 685 sourceNick = match.group(1)
686 if ( 686 if not self.isChannelName(name) and name == self.__nickName:
687 not self.isChannelName(name) and
688 name == self.__nickName
689 ):
690 if sourceNick == self.__nickName: 687 if sourceNick == self.__nickName:
691 msg = self.tr( 688 msg = self.tr(
692 "You have set your personal modes to" 689 "You have set your personal modes to" " <b>[{0}]</b>."
693 " <b>[{0}]</b>.").format(modes) 690 ).format(modes)
694 else: 691 else:
695 msg = self.tr( 692 msg = self.tr(
696 "{0} has changed your personal modes to" 693 "{0} has changed your personal modes to" " <b>[{1}]</b>."
697 " <b>[{1}]</b>.").format(sourceNick, modes) 694 ).format(sourceNick, modes)
698 self.networkWidget.addServerMessage( 695 self.networkWidget.addServerMessage(
699 self.tr("Mode"), msg, filterMsg=False) 696 self.tr("Mode"), msg, filterMsg=False
697 )
700 return True 698 return True
701 elif name == "PART": 699 elif name == "PART":
702 nick = match.group(1).split("!", 1)[0] 700 nick = match.group(1).split("!", 1)[0]
703 if nick == self.__nickName: 701 if nick == self.__nickName:
704 channel = match.group(3).split(None, 1)[0] 702 channel = match.group(3).split(None, 1)[0]
705 self.networkWidget.addMessage( 703 self.networkWidget.addMessage(
706 self.tr("You have left channel {0}.").format(channel)) 704 self.tr("You have left channel {0}.").format(channel)
705 )
707 return True 706 return True
708 elif name == "QUIT": 707 elif name == "QUIT":
709 # don't do anything with it here 708 # don't do anything with it here
710 return True 709 return True
711 elif name == "NICK": 710 elif name == "NICK":
712 # :foo_!n=foo@foohost.bar.net NICK :newnick 711 # :foo_!n=foo@foohost.bar.net NICK :newnick
713 oldNick = match.group(1).split("!", 1)[0] 712 oldNick = match.group(1).split("!", 1)[0]
714 newNick = match.group(3).split(":", 1)[1] 713 newNick = match.group(3).split(":", 1)[1]
715 if oldNick == self.__nickName: 714 if oldNick == self.__nickName:
716 self.networkWidget.addMessage( 715 self.networkWidget.addMessage(
717 self.tr("You are now known as {0}.").format(newNick)) 716 self.tr("You are now known as {0}.").format(newNick)
717 )
718 self.__nickName = newNick 718 self.__nickName = newNick
719 self.networkWidget.setNickName(newNick) 719 self.networkWidget.setNickName(newNick)
720 else: 720 else:
721 self.networkWidget.addMessage( 721 self.networkWidget.addMessage(
722 self.tr("User {0} is now known as {1}.").format( 722 self.tr("User {0} is now known as {1}.").format(oldNick, newNick)
723 oldNick, newNick)) 723 )
724 return True 724 return True
725 elif name == "PONG": 725 elif name == "PONG":
726 nick = match.group(3).split(":", 1)[1] 726 nick = match.group(3).split(":", 1)[1]
727 self.networkWidget.addMessage( 727 self.networkWidget.addMessage(
728 self.tr("Received PONG from {0}").format(nick)) 728 self.tr("Received PONG from {0}").format(nick)
729 )
729 return True 730 return True
730 elif name == "ERROR": 731 elif name == "ERROR":
731 self.networkWidget.addErrorMessage( 732 self.networkWidget.addErrorMessage(
732 self.tr("Server Error"), match.group(3).split(":", 1)[1]) 733 self.tr("Server Error"), match.group(3).split(":", 1)[1]
734 )
733 return True 735 return True
734 736
735 return False 737 return False
736 738
737 def __handleNumericMessage(self, match): 739 def __handleNumericMessage(self, match):
738 """ 740 """
739 Private method to handle a server message containing a numeric code. 741 Private method to handle a server message containing a numeric code.
740 742
741 @param match reference to the match object 743 @param match reference to the match object
742 @return flag indicating, if the message was handled (boolean) 744 @return flag indicating, if the message was handled (boolean)
743 """ 745 """
744 code = int(match.group(2)) 746 code = int(match.group(2))
745 if code < 400: 747 if code < 400:
746 return self.__handleServerReply( 748 return self.__handleServerReply(code, match.group(1), match.group(3))
747 code, match.group(1), match.group(3))
748 else: 749 else:
749 return self.__handleServerError( 750 return self.__handleServerError(code, match.group(1), match.group(3))
750 code, match.group(1), match.group(3)) 751
751
752 def __handleServerError(self, code, server, message): 752 def __handleServerError(self, code, server, message):
753 """ 753 """
754 Private slot to handle a server error reply. 754 Private slot to handle a server error reply.
755 755
756 @param code numerical code sent by the server (integer) 756 @param code numerical code sent by the server (integer)
757 @param server name of the server (string) 757 @param server name of the server (string)
758 @param message message sent by the server (string) 758 @param message message sent by the server (string)
759 @return flag indicating, if the message was handled (boolean) 759 @return flag indicating, if the message was handled (boolean)
760 """ 760 """
763 self.__handleNickInUseLogin() 763 self.__handleNickInUseLogin()
764 else: 764 else:
765 self.__handleNickInUse() 765 self.__handleNickInUse()
766 else: 766 else:
767 self.networkWidget.addServerMessage(self.tr("Error"), message) 767 self.networkWidget.addServerMessage(self.tr("Error"), message)
768 768
769 return True 769 return True
770 770
771 def __handleServerReply(self, code, server, message): 771 def __handleServerReply(self, code, server, message):
772 """ 772 """
773 Private slot to handle a server reply. 773 Private slot to handle a server reply.
774 774
775 @param code numerical code sent by the server (integer) 775 @param code numerical code sent by the server (integer)
776 @param server name of the server (string) 776 @param server name of the server (string)
777 @param message message sent by the server (string) 777 @param message message sent by the server (string)
778 @return flag indicating, if the message was handled (boolean) 778 @return flag indicating, if the message was handled (boolean)
779 """ 779 """
788 msgType = self.tr("MOTD") 788 msgType = self.tr("MOTD")
789 elif code in [305, 306]: 789 elif code in [305, 306]:
790 msgType = self.tr("Away") 790 msgType = self.tr("Away")
791 else: 791 else:
792 msgType = self.tr("Info ({0})").format(code) 792 msgType = self.tr("Info ({0})").format(code)
793 793
794 # special treatment for some messages 794 # special treatment for some messages
795 if code == 375: 795 if code == 375:
796 message = self.tr("Message of the day") 796 message = self.tr("Message of the day")
797 elif code == 376: 797 elif code == 376:
798 message = self.tr("End of message of the day") 798 message = self.tr("End of message of the day")
799 elif code == 4: 799 elif code == 4:
800 parts = message.strip().split() 800 parts = message.strip().split()
801 message = self.tr( 801 message = self.tr(
802 "Server {0} (Version {1}), User-Modes: {2}," 802 "Server {0} (Version {1}), User-Modes: {2}," " Channel-Modes: {3}"
803 " Channel-Modes: {3}"
804 ).format(parts[1], parts[2], parts[3], parts[4]) 803 ).format(parts[1], parts[2], parts[3], parts[4])
805 elif code == 265: 804 elif code == 265:
806 parts = message.strip().split() 805 parts = message.strip().split()
807 message = self.tr( 806 message = self.tr("Current users on {0}: {1}, max. {2}").format(
808 "Current users on {0}: {1}, max. {2}").format( 807 server, parts[1], parts[2]
809 server, parts[1], parts[2]) 808 )
810 elif code == 266: 809 elif code == 266:
811 parts = message.strip().split() 810 parts = message.strip().split()
812 message = self.tr( 811 message = self.tr("Current users on the network: {0}, max. {1}").format(
813 "Current users on the network: {0}, max. {1}").format( 812 parts[1], parts[2]
814 parts[1], parts[2]) 813 )
815 elif code == 305: 814 elif code == 305:
816 message = self.tr("You are no longer marked as being away.") 815 message = self.tr("You are no longer marked as being away.")
817 elif code == 306: 816 elif code == 306:
818 message = self.tr("You have been marked as being away.") 817 message = self.tr("You have been marked as being away.")
819 else: 818 else:
820 first, message = message.split(None, 1) 819 first, message = message.split(None, 1)
821 if message.startswith(":"): 820 if message.startswith(":"):
822 message = message[1:] 821 message = message[1:]
823 else: 822 else:
824 message = message.replace(":", "", 1) 823 message = message.replace(":", "", 1)
825 824
826 self.networkWidget.addServerMessage(msgType, message) 825 self.networkWidget.addServerMessage(msgType, message)
827 826
828 if code == 1: 827 if code == 1:
829 # register with services after the welcome message 828 # register with services after the welcome message
830 self.__connectionState = IrcWidget.ServerConnected 829 self.__connectionState = IrcWidget.ServerConnected
831 self.__registerWithServices() 830 self.__registerWithServices()
832 self.networkWidget.setRegistered(True) 831 self.networkWidget.setRegistered(True)
840 # extract the channel type prefixes 839 # extract the channel type prefixes
841 # ... CHANTYPES=# ... 840 # ... CHANTYPES=# ...
842 m = self.__chanTypesRe.match(message) 841 m = self.__chanTypesRe.match(message)
843 if m: 842 if m:
844 self.__setChannelTypePrefixes(m.group(1)) 843 self.__setChannelTypePrefixes(m.group(1))
845 844
846 return True 845 return True
847 846
848 def __registerWithServices(self): 847 def __registerWithServices(self):
849 """ 848 """
850 Private method to register to services. 849 Private method to register to services.
851 """ 850 """
852 identity = self.__ircNetworkManager.getIdentity(self.__identityName) 851 identity = self.__ircNetworkManager.getIdentity(self.__identityName)
853 if identity: 852 if identity:
854 service = identity.getServiceName() 853 service = identity.getServiceName()
855 password = identity.getPassword() 854 password = identity.getPassword()
856 if service and password: 855 if service and password:
857 self.__send("PRIVMSG " + service + " :identify " + password) 856 self.__send("PRIVMSG " + service + " :identify " + password)
858 857
859 def __autoJoinChannels(self): 858 def __autoJoinChannels(self):
860 """ 859 """
861 Private slot to join channels automatically once a server got 860 Private slot to join channels automatically once a server got
862 connected. 861 connected.
863 """ 862 """
864 for channel in self.networkWidget.getNetworkChannels(): 863 for channel in self.networkWidget.getNetworkChannels():
865 if channel.autoJoin(): 864 if channel.autoJoin():
866 name = channel.getName() 865 name = channel.getName()
867 key = channel.getKey() 866 key = channel.getKey()
868 self.joinChannel(name, key) 867 self.joinChannel(name, key)
869 868
870 def __tcpError(self, error): 869 def __tcpError(self, error):
871 """ 870 """
872 Private slot to handle errors reported by the TCP socket. 871 Private slot to handle errors reported by the TCP socket.
873 872
874 @param error error code reported by the socket 873 @param error error code reported by the socket
875 (QAbstractSocket.SocketError) 874 (QAbstractSocket.SocketError)
876 """ 875 """
877 if error == QAbstractSocket.SocketError.RemoteHostClosedError: 876 if error == QAbstractSocket.SocketError.RemoteHostClosedError:
878 # ignore this one, it's a disconnect 877 # ignore this one, it's a disconnect
879 if self.__sslErrorLock: 878 if self.__sslErrorLock:
880 self.networkWidget.addErrorMessage( 879 self.networkWidget.addErrorMessage(
881 self.tr("SSL Error"), 880 self.tr("SSL Error"),
882 self.tr( 881 self.tr(
883 """Connection to server {0} (port {1}) lost while""" 882 """Connection to server {0} (port {1}) lost while"""
884 """ waiting for user response to an SSL error.""") 883 """ waiting for user response to an SSL error."""
885 .format(self.__server.getName(), self.__server.getPort())) 884 ).format(self.__server.getName(), self.__server.getPort()),
885 )
886 self.__connectionState = IrcWidget.ServerDisconnected 886 self.__connectionState = IrcWidget.ServerDisconnected
887 elif error == QAbstractSocket.SocketError.HostNotFoundError: 887 elif error == QAbstractSocket.SocketError.HostNotFoundError:
888 self.networkWidget.addErrorMessage( 888 self.networkWidget.addErrorMessage(
889 self.tr("Socket Error"), 889 self.tr("Socket Error"),
890 self.tr( 890 self.tr(
891 "The host was not found. Please check the host name" 891 "The host was not found. Please check the host name"
892 " and port settings.")) 892 " and port settings."
893 ),
894 )
893 elif error == QAbstractSocket.SocketError.ConnectionRefusedError: 895 elif error == QAbstractSocket.SocketError.ConnectionRefusedError:
894 self.networkWidget.addErrorMessage( 896 self.networkWidget.addErrorMessage(
895 self.tr("Socket Error"), 897 self.tr("Socket Error"),
896 self.tr( 898 self.tr(
897 "The connection was refused by the peer. Please check the" 899 "The connection was refused by the peer. Please check the"
898 " host name and port settings.")) 900 " host name and port settings."
901 ),
902 )
899 elif error == QAbstractSocket.SocketError.SslHandshakeFailedError: 903 elif error == QAbstractSocket.SocketError.SslHandshakeFailedError:
900 self.networkWidget.addErrorMessage( 904 self.networkWidget.addErrorMessage(
901 self.tr("Socket Error"), 905 self.tr("Socket Error"), self.tr("The SSL handshake failed.")
902 self.tr("The SSL handshake failed.")) 906 )
903 else: 907 else:
904 if self.__socket: 908 if self.__socket:
905 self.networkWidget.addErrorMessage( 909 self.networkWidget.addErrorMessage(
906 self.tr("Socket Error"), 910 self.tr("Socket Error"),
907 self.tr( 911 self.tr("The following network error occurred:<br/>{0}").format(
908 "The following network error occurred:<br/>{0}") 912 self.__socket.errorString()
909 .format(self.__socket.errorString())) 913 ),
914 )
910 else: 915 else:
911 self.networkWidget.addErrorMessage( 916 self.networkWidget.addErrorMessage(
912 self.tr("Socket Error"), 917 self.tr("Socket Error"), self.tr("A network error occurred.")
913 self.tr("A network error occurred.")) 918 )
914 919
915 def __sslErrors(self, errors): 920 def __sslErrors(self, errors):
916 """ 921 """
917 Private slot to handle SSL errors. 922 Private slot to handle SSL errors.
918 923
919 @param errors list of SSL errors (list of QSslError) 924 @param errors list of SSL errors (list of QSslError)
920 """ 925 """
921 ignored, defaultChanged = self.__sslErrorHandler.sslErrors( 926 ignored, defaultChanged = self.__sslErrorHandler.sslErrors(
922 errors, self.__server.getName(), self.__server.getPort()) 927 errors, self.__server.getName(), self.__server.getPort()
928 )
923 if ignored == EricSslErrorState.NOT_IGNORED: 929 if ignored == EricSslErrorState.NOT_IGNORED:
924 self.networkWidget.addErrorMessage( 930 self.networkWidget.addErrorMessage(
925 self.tr("SSL Error"), 931 self.tr("SSL Error"),
926 self.tr( 932 self.tr(
927 """Could not connect to {0} (port {1}) using an SSL""" 933 """Could not connect to {0} (port {1}) using an SSL"""
928 """ encrypted connection. Either the server does not""" 934 """ encrypted connection. Either the server does not"""
929 """ support SSL (did you use the correct port?) or""" 935 """ support SSL (did you use the correct port?) or"""
930 """ you rejected the certificate.""") 936 """ you rejected the certificate."""
931 .format(self.__server.getName(), self.__server.getPort())) 937 ).format(self.__server.getName(), self.__server.getPort()),
938 )
932 self.__socket.close() 939 self.__socket.close()
933 else: 940 else:
934 if defaultChanged: 941 if defaultChanged:
935 self.__socket.setSslConfiguration( 942 self.__socket.setSslConfiguration(
936 QSslConfiguration.defaultConfiguration()) 943 QSslConfiguration.defaultConfiguration()
944 )
937 if ignored == EricSslErrorState.USER_IGNORED: 945 if ignored == EricSslErrorState.USER_IGNORED:
938 self.networkWidget.addErrorMessage( 946 self.networkWidget.addErrorMessage(
939 self.tr("SSL Error"), 947 self.tr("SSL Error"),
940 self.tr( 948 self.tr(
941 """The SSL certificate for the server {0} (port {1})""" 949 """The SSL certificate for the server {0} (port {1})"""
942 """ failed the authenticity check. SSL errors""" 950 """ failed the authenticity check. SSL errors"""
943 """ were accepted by you.""") 951 """ were accepted by you."""
944 .format(self.__server.getName(), self.__server.getPort())) 952 ).format(self.__server.getName(), self.__server.getPort()),
953 )
945 if self.__connectionState == IrcWidget.ServerConnecting: 954 if self.__connectionState == IrcWidget.ServerConnecting:
946 self.__socket.ignoreSslErrors() 955 self.__socket.ignoreSslErrors()
947 956
948 def __setUserPrivilegePrefix(self, prefix1, prefix2): 957 def __setUserPrivilegePrefix(self, prefix1, prefix2):
949 """ 958 """
950 Private method to set the user privilege prefix. 959 Private method to set the user privilege prefix.
951 960
952 @param prefix1 first part of the prefix (string) 961 @param prefix1 first part of the prefix (string)
953 @param prefix2 indictors the first part gets mapped to (string) 962 @param prefix2 indictors the first part gets mapped to (string)
954 """ 963 """
955 # PREFIX=(ov)@+ 964 # PREFIX=(ov)@+
956 # o = @ -> @ircbot , channel operator 965 # o = @ -> @ircbot , channel operator
957 # v = + -> +userName , voice operator 966 # v = + -> +userName , voice operator
958 for i in range(len(prefix1)): 967 for i in range(len(prefix1)):
959 self.__userPrefix["+" + prefix1[i]] = prefix2[i] 968 self.__userPrefix["+" + prefix1[i]] = prefix2[i]
960 self.__userPrefix["-" + prefix1[i]] = "" 969 self.__userPrefix["-" + prefix1[i]] = ""
961 970
962 def __ping(self, match): 971 def __ping(self, match):
963 """ 972 """
964 Private method to handle a PING message. 973 Private method to handle a PING message.
965 974
966 @param match reference to the match object 975 @param match reference to the match object
967 @return flag indicating, if the message was handled (boolean) 976 @return flag indicating, if the message was handled (boolean)
968 """ 977 """
969 self.__send("PONG " + match.group(1)) 978 self.__send("PONG " + match.group(1))
970 return True 979 return True
971 980
972 def __handleCtcp(self, match): 981 def __handleCtcp(self, match):
973 """ 982 """
974 Private method to handle a CTCP command. 983 Private method to handle a CTCP command.
975 984
976 @param match reference to the match object 985 @param match reference to the match object
977 @return flag indicating, if the message was handled (boolean) 986 @return flag indicating, if the message was handled (boolean)
978 """ 987 """
979 # group(1) sender user name 988 # group(1) sender user name
980 # group(2) sender user@host 989 # group(2) sender user@host
994 vers = " " + Version 1003 vers = " " + Version
995 msg = "Eric IRC client{0}, {1}".format(vers, Copyright) 1004 msg = "Eric IRC client{0}, {1}".format(vers, Copyright)
996 self.networkWidget.addServerMessage( 1005 self.networkWidget.addServerMessage(
997 self.tr("CTCP"), 1006 self.tr("CTCP"),
998 self.tr("Received Version request from {0}.").format( 1007 self.tr("Received Version request from {0}.").format(
999 match.group(1))) 1008 match.group(1)
1009 ),
1010 )
1000 self.__sendCtcpReply(match.group(1), "VERSION " + msg) 1011 self.__sendCtcpReply(match.group(1), "VERSION " + msg)
1001 elif ctcpRequest == "ping": 1012 elif ctcpRequest == "ping":
1002 self.networkWidget.addServerMessage( 1013 self.networkWidget.addServerMessage(
1003 self.tr("CTCP"), 1014 self.tr("CTCP"),
1004 self.tr( 1015 self.tr(
1005 "Received CTCP-PING request from {0}," 1016 "Received CTCP-PING request from {0}," " sending answer."
1006 " sending answer.").format(match.group(1))) 1017 ).format(match.group(1)),
1007 self.__sendCtcpReply( 1018 )
1008 match.group(1), "PING {0}".format(ctcpArg)) 1019 self.__sendCtcpReply(match.group(1), "PING {0}".format(ctcpArg))
1009 elif ctcpRequest == "clientinfo": 1020 elif ctcpRequest == "clientinfo":
1010 self.networkWidget.addServerMessage( 1021 self.networkWidget.addServerMessage(
1011 self.tr("CTCP"), 1022 self.tr("CTCP"),
1012 self.tr( 1023 self.tr(
1013 "Received CTCP-CLIENTINFO request from {0}," 1024 "Received CTCP-CLIENTINFO request from {0}," " sending answer."
1014 " sending answer.").format(match.group(1))) 1025 ).format(match.group(1)),
1026 )
1015 self.__sendCtcpReply( 1027 self.__sendCtcpReply(
1016 match.group(1), 1028 match.group(1), "CLIENTINFO CLIENTINFO PING VERSION"
1017 "CLIENTINFO CLIENTINFO PING VERSION") 1029 )
1018 else: 1030 else:
1019 self.networkWidget.addServerMessage( 1031 self.networkWidget.addServerMessage(
1020 self.tr("CTCP"), 1032 self.tr("CTCP"),
1021 self.tr( 1033 self.tr("Received unknown CTCP-{0} request from {1}.").format(
1022 "Received unknown CTCP-{0} request from {1}.") 1034 ctcpRequest, match.group(1)
1023 .format(ctcpRequest, match.group(1))) 1035 ),
1036 )
1024 return True 1037 return True
1025 1038
1026 return False 1039 return False
1027 1040
1028 def __updateUsersCount(self): 1041 def __updateUsersCount(self):
1029 """ 1042 """
1030 Private method to update the users count on the channel tabs. 1043 Private method to update the users count on the channel tabs.
1031 """ 1044 """
1032 for channel in self.__channelList: 1045 for channel in self.__channelList:
1033 index = self.channelsWidget.indexOf(channel) 1046 index = self.channelsWidget.indexOf(channel)
1034 self.channelsWidget.setTabText( 1047 self.channelsWidget.setTabText(
1035 index, 1048 index,
1036 self.tr("{0} ({1})", "channel name, users count").format( 1049 self.tr("{0} ({1})", "channel name, users count").format(
1037 channel.name(), channel.getUsersCount())) 1050 channel.name(), channel.getUsersCount()
1038 1051 ),
1052 )
1053
1039 def __handleNickInUseLogin(self): 1054 def __handleNickInUseLogin(self):
1040 """ 1055 """
1041 Private method to handle a 443 server error at login. 1056 Private method to handle a 443 server error at login.
1042 """ 1057 """
1043 self.__nickIndex += 1 1058 self.__nickIndex += 1
1044 try: 1059 try:
1045 identity = self.__ircNetworkManager.getIdentity( 1060 identity = self.__ircNetworkManager.getIdentity(self.__identityName)
1046 self.__identityName)
1047 if identity: 1061 if identity:
1048 nick = identity.getNickNames()[self.__nickIndex] 1062 nick = identity.getNickNames()[self.__nickIndex]
1049 self.__nickName = nick 1063 self.__nickName = nick
1050 else: 1064 else:
1051 self.__connectNetwork("", False, True) 1065 self.__connectNetwork("", False, True)
1055 except IndexError: 1069 except IndexError:
1056 self.networkWidget.addServerMessage( 1070 self.networkWidget.addServerMessage(
1057 self.tr("Critical"), 1071 self.tr("Critical"),
1058 self.tr( 1072 self.tr(
1059 "No nickname acceptable to the server configured" 1073 "No nickname acceptable to the server configured"
1060 " for <b>{0}</b>. Disconnecting...") 1074 " for <b>{0}</b>. Disconnecting..."
1061 .format(self.__userName), 1075 ).format(self.__userName),
1062 filterMsg=False) 1076 filterMsg=False,
1077 )
1063 self.__connectNetwork("", False, True) 1078 self.__connectNetwork("", False, True)
1064 self.__nickName = "" 1079 self.__nickName = ""
1065 self.__nickIndex = -1 1080 self.__nickIndex = -1
1066 return 1081 return
1067 1082
1068 self.networkWidget.setNickName(nick) 1083 self.networkWidget.setNickName(nick)
1069 self.__send("NICK " + nick) 1084 self.__send("NICK " + nick)
1070 1085
1071 def __handleNickInUse(self): 1086 def __handleNickInUse(self):
1072 """ 1087 """
1073 Private method to handle a 443 server error. 1088 Private method to handle a 443 server error.
1074 """ 1089 """
1075 self.networkWidget.addServerMessage( 1090 self.networkWidget.addServerMessage(
1076 self.tr("Critical"), 1091 self.tr("Critical"), self.tr("The given nickname is already in use.")
1077 self.tr("The given nickname is already in use.")) 1092 )
1078 1093
1079 def __changeNick(self, nick): 1094 def __changeNick(self, nick):
1080 """ 1095 """
1081 Private slot to use a new nick name. 1096 Private slot to use a new nick name.
1082 1097
1083 @param nick nick name to use (str) 1098 @param nick nick name to use (str)
1084 """ 1099 """
1085 if nick and nick != self.__nickName: 1100 if nick and nick != self.__nickName:
1086 self.__send("NICK " + nick) 1101 self.__send("NICK " + nick)
1087 1102
1088 def __setChannelTypePrefixes(self, prefixes): 1103 def __setChannelTypePrefixes(self, prefixes):
1089 """ 1104 """
1090 Private method to set the channel type prefixes. 1105 Private method to set the channel type prefixes.
1091 1106
1092 @param prefixes channel prefix characters (string) 1107 @param prefixes channel prefix characters (string)
1093 """ 1108 """
1094 self.__channelTypePrefixes = prefixes 1109 self.__channelTypePrefixes = prefixes
1095 1110
1096 def isChannelName(self, name): 1111 def isChannelName(self, name):
1097 """ 1112 """
1098 Public method to check, if the given name is a channel name. 1113 Public method to check, if the given name is a channel name.
1099 1114
1100 @param name name to check (string) 1115 @param name name to check (string)
1101 @return flag indicating a channel name (boolean) 1116 @return flag indicating a channel name (boolean)
1102 """ 1117 """
1103 if not name: 1118 if not name:
1104 return False 1119 return False
1105 1120
1106 if self.__channelTypePrefixes: 1121 if self.__channelTypePrefixes:
1107 return name[0] in self.__channelTypePrefixes 1122 return name[0] in self.__channelTypePrefixes
1108 else: 1123 else:
1109 return name[0] in "#&" 1124 return name[0] in "#&"
1110 1125
1111 def __away(self, isAway): 1126 def __away(self, isAway):
1112 """ 1127 """
1113 Private slot handling the change of the away state. 1128 Private slot handling the change of the away state.
1114 1129
1115 @param isAway flag indicating the current away state (boolean) 1130 @param isAway flag indicating the current away state (boolean)
1116 """ 1131 """
1117 if isAway and self.__identityName: 1132 if isAway and self.__identityName:
1118 identity = self.__ircNetworkManager.getIdentity( 1133 identity = self.__ircNetworkManager.getIdentity(self.__identityName)
1119 self.__identityName)
1120 if identity and identity.rememberAwayPosition(): 1134 if identity and identity.rememberAwayPosition():
1121 for channel in self.__channelList: 1135 for channel in self.__channelList:
1122 channel.setMarkerLine() 1136 channel.setMarkerLine()

eric ide

mercurial