12 |
12 |
13 from E5Gui import E5MessageBox |
13 from E5Gui import E5MessageBox |
14 |
14 |
15 import Preferences |
15 import Preferences |
16 |
16 |
17 MaxBufferSize = 1024 * 1024 |
17 MaxBufferSize = 1024 * 1024 |
18 TransferTimeout = 30 * 1000 |
18 TransferTimeout = 30 * 1000 |
19 PongTimeout = 60 * 1000 |
19 PongTimeout = 60 * 1000 |
20 PingInterval = 5 * 1000 |
20 PingInterval = 5 * 1000 |
21 SeparatorToken = '|||' |
21 SeparatorToken = '|||' |
|
22 |
22 |
23 |
23 class Connection(QTcpSocket): |
24 class Connection(QTcpSocket): |
24 """ |
25 """ |
25 Class representing a peer connection. |
26 Class representing a peer connection. |
26 |
27 |
27 @signal readyForUse() emitted when the connection is ready for use |
28 @signal readyForUse() emitted when the connection is ready for use |
28 @signal newMessage(user, message) emitted after a new message has |
29 @signal newMessage(user, message) emitted after a new message has |
29 arrived (string, string) |
30 arrived (string, string) |
30 @signal getParticipants() emitted after a get participants message has arrived |
31 @signal getParticipants() emitted after a get participants message has arrived |
31 @signal participants(participants) emitted after the list of participants has |
32 @signal participants(participants) emitted after the list of participants has |
32 arrived (list of strings of "host:port") |
33 arrived (list of strings of "host:port") |
33 """ |
34 """ |
34 WaitingForGreeting = 0 |
35 WaitingForGreeting = 0 |
35 ReadingGreeting = 1 |
36 ReadingGreeting = 1 |
36 ReadyForUse = 2 |
37 ReadyForUse = 2 |
37 |
38 |
38 PlainText = 0 |
39 PlainText = 0 |
39 Ping = 1 |
40 Ping = 1 |
40 Pong = 2 |
41 Pong = 2 |
41 Greeting = 3 |
42 Greeting = 3 |
42 GetParticipants = 4 |
43 GetParticipants = 4 |
43 Participants = 5 |
44 Participants = 5 |
44 Editor = 6 |
45 Editor = 6 |
45 Undefined = 99 |
46 Undefined = 99 |
46 |
47 |
47 ProtocolMessage = "MESSAGE" |
48 ProtocolMessage = "MESSAGE" |
48 ProtocolPing = "PING" |
49 ProtocolPing = "PING" |
49 ProtocolPong = "PONG" |
50 ProtocolPong = "PONG" |
50 ProtocolGreeting = "GREETING" |
51 ProtocolGreeting = "GREETING" |
51 ProtocolGetParticipants = "GET_PARTICIPANTS" |
52 ProtocolGetParticipants = "GET_PARTICIPANTS" |
52 ProtocolParticipants = "PARTICIPANTS" |
53 ProtocolParticipants = "PARTICIPANTS" |
53 ProtocolEditor = "EDITOR" |
54 ProtocolEditor = "EDITOR" |
54 |
55 |
55 readyForUse = pyqtSignal() |
56 readyForUse = pyqtSignal() |
56 newMessage = pyqtSignal(str, str) |
57 newMessage = pyqtSignal(str, str) |
57 getParticipants = pyqtSignal() |
58 getParticipants = pyqtSignal() |
58 participants = pyqtSignal(list) |
59 participants = pyqtSignal(list) |
59 editorCommand = pyqtSignal(str, str, str) |
60 editorCommand = pyqtSignal(str, str, str) |
60 rejected = pyqtSignal(str) |
61 rejected = pyqtSignal(str) |
61 |
62 |
62 def __init__(self, parent = None): |
63 def __init__(self, parent=None): |
63 """ |
64 """ |
64 Constructor |
65 Constructor |
65 |
66 |
66 @param parent referenec to the parent object (QObject) |
67 @param parent referenec to the parent object (QObject) |
67 """ |
68 """ |
155 if self.__buffer.size() != self.__numBytesForCurrentDataType: |
156 if self.__buffer.size() != self.__numBytesForCurrentDataType: |
156 self.abort() |
157 self.abort() |
157 return |
158 return |
158 |
159 |
159 try: |
160 try: |
160 user, serverPort = str(self.__buffer, encoding = "utf-8").split(":") |
161 user, serverPort = str(self.__buffer, encoding="utf-8").split(":") |
161 except ValueError: |
162 except ValueError: |
162 self.abort() |
163 self.abort() |
163 return |
164 return |
164 self.__serverPort = int(serverPort) |
165 self.__serverPort = int(serverPort) |
165 |
166 |
166 self.__username = "{0}@{1}:{2}".format( |
167 self.__username = "{0}@{1}:{2}".format( |
167 user, |
168 user, |
168 self.peerAddress().toString(), |
169 self.peerAddress().toString(), |
169 self.peerPort() |
170 self.peerPort() |
170 ) |
171 ) |
171 self.__currentDataType = Connection.Undefined |
172 self.__currentDataType = Connection.Undefined |
172 self.__numBytesForCurrentDataType = 0 |
173 self.__numBytesForCurrentDataType = 0 |
173 self.__buffer.clear() |
174 self.__buffer.clear() |
188 self.abort() |
189 self.abort() |
189 return |
190 return |
190 |
191 |
191 if self.__serverPort != self.peerPort() and \ |
192 if self.__serverPort != self.peerPort() and \ |
192 not Preferences.getCooperation("AutoAcceptConnections"): |
193 not Preferences.getCooperation("AutoAcceptConnections"): |
193 # don't ask for reverse connections or |
194 # don't ask for reverse connections or |
194 # if we shall accept automatically |
195 # if we shall accept automatically |
195 res = E5MessageBox.yesNo(None, |
196 res = E5MessageBox.yesNo(None, |
196 self.trUtf8("New Connection"), |
197 self.trUtf8("New Connection"), |
197 self.trUtf8("""<p>Accept connection from """ |
198 self.trUtf8("""<p>Accept connection from """ |
198 """<strong>{0}@{1}</strong>?</p>""").format( |
199 """<strong>{0}@{1}</strong>?</p>""").format( |
199 user, self.peerAddress().toString()), |
200 user, self.peerAddress().toString()), |
200 yesDefault = True) |
201 yesDefault=True) |
201 if not res: |
202 if not res: |
202 self.abort() |
203 self.abort() |
203 return |
204 return |
204 |
205 |
205 if not self.__isGreetingMessageSent: |
206 if not self.__isGreetingMessageSent: |
238 data = QByteArray("{0}{1}{2}{1}".format( |
239 data = QByteArray("{0}{1}{2}{1}".format( |
239 Connection.ProtocolGreeting, SeparatorToken, greeting.size())) + greeting |
240 Connection.ProtocolGreeting, SeparatorToken, greeting.size())) + greeting |
240 if self.write(data) == data.size(): |
241 if self.write(data) == data.size(): |
241 self.__isGreetingMessageSent = True |
242 self.__isGreetingMessageSent = True |
242 |
243 |
243 def __readDataIntoBuffer(self, maxSize = MaxBufferSize): |
244 def __readDataIntoBuffer(self, maxSize=MaxBufferSize): |
244 """ |
245 """ |
245 Private method to read some data into the buffer. |
246 Private method to read some data into the buffer. |
246 |
247 |
247 @param maxSize maximum size of data to read (integer) |
248 @param maxSize maximum size of data to read (integer) |
248 @return size of data read (integer) |
249 @return size of data read (integer) |
344 if self.__buffer.size() != self.__numBytesForCurrentDataType: |
345 if self.__buffer.size() != self.__numBytesForCurrentDataType: |
345 self.abort() |
346 self.abort() |
346 return |
347 return |
347 |
348 |
348 if self.__currentDataType == Connection.PlainText: |
349 if self.__currentDataType == Connection.PlainText: |
349 self.newMessage.emit(self.__username, str(self.__buffer, encoding = "utf-8")) |
350 self.newMessage.emit(self.__username, str(self.__buffer, encoding="utf-8")) |
350 elif self.__currentDataType == Connection.Ping: |
351 elif self.__currentDataType == Connection.Ping: |
351 self.write("{0}{1}1{1}p".format(Connection.ProtocolPong, SeparatorToken)) |
352 self.write("{0}{1}1{1}p".format(Connection.ProtocolPong, SeparatorToken)) |
352 elif self.__currentDataType == Connection.Pong: |
353 elif self.__currentDataType == Connection.Pong: |
353 self.__pongTime.restart() |
354 self.__pongTime.restart() |
354 elif self.__currentDataType == Connection.GetParticipants: |
355 elif self.__currentDataType == Connection.GetParticipants: |
355 self.getParticipants.emit() |
356 self.getParticipants.emit() |
356 elif self.__currentDataType == Connection.Participants: |
357 elif self.__currentDataType == Connection.Participants: |
357 msg = str(self.__buffer, encoding = "utf-8") |
358 msg = str(self.__buffer, encoding="utf-8") |
358 if msg == "<empty>": |
359 if msg == "<empty>": |
359 participantsList = [] |
360 participantsList = [] |
360 else: |
361 else: |
361 participantsList = msg.split(SeparatorToken) |
362 participantsList = msg.split(SeparatorToken) |
362 self.participants.emit(participantsList[:]) |
363 self.participants.emit(participantsList[:]) |
363 elif self.__currentDataType == Connection.Editor: |
364 elif self.__currentDataType == Connection.Editor: |
364 hash, fn, msg = str(self.__buffer, encoding = "utf-8").split(SeparatorToken) |
365 hash, fn, msg = str(self.__buffer, encoding="utf-8").split(SeparatorToken) |
365 self.editorCommand.emit(hash, fn, msg) |
366 self.editorCommand.emit(hash, fn, msg) |
366 |
367 |
367 self.__currentDataType = Connection.Undefined |
368 self.__currentDataType = Connection.Undefined |
368 self.__numBytesForCurrentDataType = 0 |
369 self.__numBytesForCurrentDataType = 0 |
369 self.__buffer.clear() |
370 self.__buffer.clear() |
394 def sendEditorCommand(self, projectHash, filename, message): |
395 def sendEditorCommand(self, projectHash, filename, message): |
395 """ |
396 """ |
396 Public method to send an editor command. |
397 Public method to send an editor command. |
397 |
398 |
398 @param projectHash hash of the project (string) |
399 @param projectHash hash of the project (string) |
399 @param filename project relative universal file name of |
400 @param filename project relative universal file name of |
400 the sending editor (string) |
401 the sending editor (string) |
401 @param message editor command to be sent (string) |
402 @param message editor command to be sent (string) |
402 """ |
403 """ |
403 msg = QByteArray("{0}{1}{2}{1}{3}".format( |
404 msg = QByteArray("{0}{1}{2}{1}{3}".format( |
404 projectHash, SeparatorToken, filename, message).encode("utf-8")) |
405 projectHash, SeparatorToken, filename, message).encode("utf-8")) |