--- a/src/eric7/Cooperation/Connection.py Wed Jul 13 11:16:20 2022 +0200 +++ b/src/eric7/Cooperation/Connection.py Wed Jul 13 14:55:47 2022 +0200 @@ -19,14 +19,14 @@ TransferTimeout = 30 * 1000 PongTimeout = 60 * 1000 PingInterval = 5 * 1000 -SeparatorToken = '|||' -SeparatorToken_b = b'|||' +SeparatorToken = "|||" +SeparatorToken_b = b"|||" class Connection(QTcpSocket): """ Class representing a peer connection. - + @signal readyForUse() emitted when the connection is ready for use @signal newMessage(user, message) emitted after a new message has arrived (string, string) @@ -39,10 +39,11 @@ @signal rejected(message) emitted after a connection has been rejected (string) """ + WaitingForGreeting = 0 ReadingGreeting = 1 ReadyForUse = 2 - + PlainText = 0 Ping = 1 Pong = 2 @@ -51,7 +52,7 @@ Participants = 5 Editor = 6 Undefined = 99 - + ProtocolMessage = "MESSAGE" ProtocolPing = "PING" ProtocolPong = "PONG" @@ -59,22 +60,22 @@ ProtocolGetParticipants = "GET_PARTICIPANTS" ProtocolParticipants = "PARTICIPANTS" ProtocolEditor = "EDITOR" - + readyForUse = pyqtSignal() newMessage = pyqtSignal(str, str) getParticipants = pyqtSignal() participants = pyqtSignal(list) editorCommand = pyqtSignal(str, str, str) rejected = pyqtSignal(str) - + def __init__(self, parent=None): """ Constructor - + @param parent referenec to the parent object (QObject) """ super().__init__(parent) - + self.__greetingMessage = self.tr("undefined") self.__username = self.tr("unknown") self.__serverPort = 0 @@ -88,72 +89,77 @@ self.__pongTime = QTime() self.__buffer = QByteArray() self.__client = None - + self.readyRead.connect(self.__processReadyRead) self.disconnected.connect(self.__disconnected) self.__pingTimer.timeout.connect(self.__sendPing) self.connected.connect(self.__sendGreetingMessage) - + def name(self): """ Public method to get the connection name. - + @return connection name (string) """ return self.__username - + def serverPort(self): """ Public method to get the server port. - + @return server port (integer) """ return self.__serverPort - + def setClient(self, client): """ Public method to set the reference to the cooperation client. - + @param client reference to the cooperation client (CooperationClient) """ self.__client = client - + def setGreetingMessage(self, message, serverPort): """ Public method to set the greeting message. - + @param message greeting message (string) @param serverPort port number to include in the message (integer) """ self.__greetingMessage = "{0}:{1}".format(message, serverPort) - + def sendMessage(self, message): """ Public method to send a message. - + @param message message to be sent (string) @return flag indicating a successful send (boolean) """ if message == "": return False - + msg = QByteArray(message.encode("utf-8")) - data = QByteArray("{0}{1}{2}{1}".format( - Connection.ProtocolMessage, SeparatorToken, msg.size()) - .encode("utf-8")) + msg + data = ( + QByteArray( + "{0}{1}{2}{1}".format( + Connection.ProtocolMessage, SeparatorToken, msg.size() + ).encode("utf-8") + ) + + msg + ) return self.write(data) == data.size() - + def timerEvent(self, evt): """ Protected method to handle timer events. - + @param evt reference to the timer event (QTimerEvent) """ if evt.timerId() == self.__transferTimerId: self.abort() self.killTimer(self.__transferTimerId) self.__transferTimerId = 0 - + def __processReadyRead(self): """ Private slot to handle the readyRead signal. @@ -165,62 +171,63 @@ self.abort() return self.__state = Connection.ReadingGreeting - + if self.__state == Connection.ReadingGreeting: if not self.__hasEnoughData(): return - - self.__buffer = QByteArray( - self.read(self.__numBytesForCurrentDataType)) + + self.__buffer = QByteArray(self.read(self.__numBytesForCurrentDataType)) if self.__buffer.size() != self.__numBytesForCurrentDataType: self.abort() return - + try: - user, serverPort = ( - str(self.__buffer, encoding="utf-8").split(":")) + user, serverPort = str(self.__buffer, encoding="utf-8").split(":") except ValueError: self.abort() return self.__serverPort = int(serverPort) - + hostInfo = QHostInfo.fromName(self.peerAddress().toString()) self.__username = "{0}@{1}@{2}".format( - user, - hostInfo.hostName(), - self.peerPort() + user, hostInfo.hostName(), self.peerPort() ) self.__currentDataType = Connection.Undefined self.__numBytesForCurrentDataType = 0 self.__buffer.clear() - + if not self.isValid(): self.abort() return - + bannedName = "{0}@{1}".format( user, hostInfo.hostName(), ) Preferences.syncPreferences() if bannedName in Preferences.getCooperation("BannedUsers"): - self.rejected.emit(self.tr( - "* Connection attempted by banned user '{0}'.") - .format(bannedName)) + self.rejected.emit( + self.tr("* Connection attempted by banned user '{0}'.").format( + bannedName + ) + ) self.abort() return - - if (self.__serverPort != self.peerPort() and - not Preferences.getCooperation("AutoAcceptConnections")): + + if self.__serverPort != self.peerPort() and not Preferences.getCooperation( + "AutoAcceptConnections" + ): # don't ask for reverse connections or # if we shall accept automatically res = EricMessageBox.yesNo( None, self.tr("New Connection"), - self.tr("""<p>Accept connection from """ - """<strong>{0}@{1}</strong>?</p>""").format( - user, hostInfo.hostName()), - yesDefault=True) + self.tr( + """<p>Accept connection from """ + """<strong>{0}@{1}</strong>?</p>""" + ).format(user, hostInfo.hostName()), + yesDefault=True, + ) if not res: self.abort() return @@ -228,29 +235,28 @@ if self.__client is not None: chatWidget = self.__client.chatWidget() if chatWidget is not None and not chatWidget.isVisible(): - ericApp().getObject( - "UserInterface").activateCooperationViewer() - + ericApp().getObject("UserInterface").activateCooperationViewer() + if not self.__isGreetingMessageSent: self.__sendGreetingMessage() - + self.__pingTimer.start() self.__pongTime = QTime.currentTime() self.__state = Connection.ReadyForUse self.readyForUse.emit() - + while self.bytesAvailable(): if ( - self.__currentDataType == Connection.Undefined and - not self.__readProtocolHeader() + self.__currentDataType == Connection.Undefined + and not self.__readProtocolHeader() ): return - + if not self.__hasEnoughData(): return - + self.__processData() - + def __sendPing(self): """ Private slot to send a ping message. @@ -258,73 +264,85 @@ if self.__pongTime.msecsTo(QTime.currentTime()) > PongTimeout: self.abort() return - - self.write(QByteArray("{0}{1}1{1}p".format( - Connection.ProtocolPing, SeparatorToken).encode("utf-8"))) - + + self.write( + QByteArray( + "{0}{1}1{1}p".format(Connection.ProtocolPing, SeparatorToken).encode( + "utf-8" + ) + ) + ) + def __sendGreetingMessage(self): """ Private slot to send a greeting message. """ greeting = QByteArray(self.__greetingMessage.encode("utf-8")) - data = QByteArray("{0}{1}{2}{1}".format( - Connection.ProtocolGreeting, SeparatorToken, greeting.size()) - .encode("utf-8")) + greeting + data = ( + QByteArray( + "{0}{1}{2}{1}".format( + Connection.ProtocolGreeting, SeparatorToken, greeting.size() + ).encode("utf-8") + ) + + greeting + ) if self.write(data) == data.size(): self.__isGreetingMessageSent = True - + def __readDataIntoBuffer(self, maxSize=MaxBufferSize): """ Private method to read some data into the buffer. - + @param maxSize maximum size of data to read (integer) @return size of data read (integer) """ if maxSize > MaxBufferSize: return 0 - + numBytesBeforeRead = self.__buffer.size() if numBytesBeforeRead == MaxBufferSize: self.abort() return 0 - + while self.bytesAvailable() and self.__buffer.size() < maxSize: self.__buffer.append(self.read(1)) if self.__buffer.endsWith(SeparatorToken_b): break - + return self.__buffer.size() - numBytesBeforeRead - + def __dataLengthForCurrentDataType(self): """ Private method to get the data length for the current data type. - + @return data length (integer) """ - if (self.bytesAvailable() <= 0 or - self.__readDataIntoBuffer() <= 0 or - not self.__buffer.endsWith(SeparatorToken_b)): + if ( + self.bytesAvailable() <= 0 + or self.__readDataIntoBuffer() <= 0 + or not self.__buffer.endsWith(SeparatorToken_b) + ): return 0 - + self.__buffer.chop(len(SeparatorToken_b)) number = self.__buffer.toInt()[0] self.__buffer.clear() return number - + def __readProtocolHeader(self): """ Private method to read the protocol header. - + @return flag indicating a successful read (boolean) """ if self.__transferTimerId: self.killTimer(self.__transferTimerId) self.__transferTimerId = 0 - + if self.__readDataIntoBuffer() <= 0: self.__transferTimerId = self.startTimer(TransferTimeout) return False - + self.__buffer.chop(len(SeparatorToken)) protocolHeader = str(self.__buffer, encoding="utf-8") if protocolHeader == Connection.ProtocolPing: @@ -345,49 +363,52 @@ self.__currentDataType = Connection.Undefined self.abort() return False - + self.__buffer.clear() - self.__numBytesForCurrentDataType = ( - self.__dataLengthForCurrentDataType()) + self.__numBytesForCurrentDataType = self.__dataLengthForCurrentDataType() return True - + def __hasEnoughData(self): """ Private method to check, if enough data is available. - + @return flag indicating availability of enough data (boolean) """ if self.__transferTimerId: self.killTimer(self.__transferTimerId) self.__transferTimerId = 0 - + if self.__numBytesForCurrentDataType <= 0: - self.__numBytesForCurrentDataType = ( - self.__dataLengthForCurrentDataType()) - - if (self.bytesAvailable() < self.__numBytesForCurrentDataType or - self.__numBytesForCurrentDataType <= 0): + self.__numBytesForCurrentDataType = self.__dataLengthForCurrentDataType() + + if ( + self.bytesAvailable() < self.__numBytesForCurrentDataType + or self.__numBytesForCurrentDataType <= 0 + ): self.__transferTimerId = self.startTimer(TransferTimeout) return False - + return True - + def __processData(self): """ Private method to process the received data. """ - self.__buffer = QByteArray( - self.read(self.__numBytesForCurrentDataType)) + self.__buffer = QByteArray(self.read(self.__numBytesForCurrentDataType)) if self.__buffer.size() != self.__numBytesForCurrentDataType: self.abort() return - + if self.__currentDataType == Connection.PlainText: - self.newMessage.emit( - self.__username, str(self.__buffer, encoding="utf-8")) + self.newMessage.emit(self.__username, str(self.__buffer, encoding="utf-8")) elif self.__currentDataType == Connection.Ping: - self.write(QByteArray("{0}{1}1{1}p".format( - Connection.ProtocolPong, SeparatorToken).encode("utf-8"))) + self.write( + QByteArray( + "{0}{1}1{1}p".format( + Connection.ProtocolPong, SeparatorToken + ).encode("utf-8") + ) + ) elif self.__currentDataType == Connection.Pong: self.__pongTime = QTime.currentTime() elif self.__currentDataType == Connection.GetParticipants: @@ -400,61 +421,78 @@ participantsList = msg.split(SeparatorToken) self.participants.emit(participantsList[:]) elif self.__currentDataType == Connection.Editor: - hashStr, fn, msg = ( - str(self.__buffer, encoding="utf-8").split(SeparatorToken)) + hashStr, fn, msg = str(self.__buffer, encoding="utf-8").split( + SeparatorToken + ) self.editorCommand.emit(hashStr, fn, msg) - + self.__currentDataType = Connection.Undefined self.__numBytesForCurrentDataType = 0 self.__buffer.clear() - + def sendGetParticipants(self): """ Public method to request a list of participants. """ - self.write(QByteArray( - "{0}{1}1{1}l".format( - Connection.ProtocolGetParticipants, SeparatorToken - ).encode("utf-8") - )) - + self.write( + QByteArray( + "{0}{1}1{1}l".format( + Connection.ProtocolGetParticipants, SeparatorToken + ).encode("utf-8") + ) + ) + def sendParticipants(self, participants): """ Public method to send the list of participants. - + @param participants list of participants (list of strings of "host:port") """ - message = (SeparatorToken.join(participants) if participants - else "<empty>") + message = SeparatorToken.join(participants) if participants else "<empty>" msg = QByteArray(message.encode("utf-8")) - data = QByteArray("{0}{1}{2}{1}".format( - Connection.ProtocolParticipants, SeparatorToken, msg.size()) - .encode("utf-8")) + msg + data = ( + QByteArray( + "{0}{1}{2}{1}".format( + Connection.ProtocolParticipants, SeparatorToken, msg.size() + ).encode("utf-8") + ) + + msg + ) self.write(data) - + def sendEditorCommand(self, projectHash, filename, message): """ Public method to send an editor command. - + @param projectHash hash of the project (string) @param filename project relative universal file name of the sending editor (string) @param message editor command to be sent (string) """ - msg = QByteArray("{0}{1}{2}{1}{3}".format( - projectHash, SeparatorToken, filename, message).encode("utf-8")) - data = QByteArray("{0}{1}{2}{1}".format( - Connection.ProtocolEditor, SeparatorToken, msg.size()) - .encode("utf-8")) + msg + msg = QByteArray( + "{0}{1}{2}{1}{3}".format( + projectHash, SeparatorToken, filename, message + ).encode("utf-8") + ) + data = ( + QByteArray( + "{0}{1}{2}{1}".format( + Connection.ProtocolEditor, SeparatorToken, msg.size() + ).encode("utf-8") + ) + + msg + ) self.write(data) - + def __disconnected(self): """ Private slot to handle the connection being dropped. """ self.__pingTimer.stop() if self.__state == Connection.WaitingForGreeting: - self.rejected.emit(self.tr( - "* Connection to {0}:{1} refused.").format( - self.peerName(), self.peerPort())) + self.rejected.emit( + self.tr("* Connection to {0}:{1} refused.").format( + self.peerName(), self.peerPort() + ) + )