8 """ |
8 """ |
9 |
9 |
10 import collections |
10 import collections |
11 |
11 |
12 from PyQt4.QtCore import QObject, pyqtSignal, QProcess, QRegExp |
12 from PyQt4.QtCore import QObject, pyqtSignal, QProcess, QRegExp |
13 from PyQt4.QtNetwork import QHostInfo, QHostAddress, QAbstractSocket |
13 from PyQt4.QtNetwork import QHostInfo, QHostAddress, QAbstractSocket, QNetworkInterface |
14 |
14 |
15 from .CooperationServer import CooperationServer |
15 from .CooperationServer import CooperationServer |
16 from .Connection import Connection |
16 from .Connection import Connection |
17 |
17 |
18 import Preferences |
18 import Preferences |
44 |
44 |
45 @param parent reference to the parent object (QObject) |
45 @param parent reference to the parent object (QObject) |
46 """ |
46 """ |
47 super().__init__(parent) |
47 super().__init__(parent) |
48 |
48 |
49 self.__server = CooperationServer(self) |
49 self.__chatWidget = parent |
|
50 |
|
51 self.__servers = [] |
|
52 for networkInterface in QNetworkInterface.allInterfaces(): |
|
53 for addressEntry in networkInterface.addressEntries(): |
|
54 address = addressEntry.ip() |
|
55 # fix scope of link local addresses |
|
56 if address.toString().lower().startswith("fe80"): |
|
57 address.setScopeId(networkInterface.humanReadableName()) |
|
58 server = CooperationServer(address, self) |
|
59 server.newConnection.connect(self.__newConnection) |
|
60 self.__servers.append(server) |
|
61 |
50 self.__peers = collections.defaultdict(list) |
62 self.__peers = collections.defaultdict(list) |
51 |
63 |
52 self.__initialConnection = None |
64 self.__initialConnection = None |
53 |
65 |
54 envVariables = ["USERNAME.*", "USER.*", "USERDOMAIN.*", |
66 envVariables = ["USERNAME.*", "USER.*", "USERDOMAIN.*", |
68 break |
80 break |
69 |
81 |
70 if self.__username == "": |
82 if self.__username == "": |
71 self.__username = self.trUtf8("unknown") |
83 self.__username = self.trUtf8("unknown") |
72 |
84 |
73 self.__server.newConnection.connect(self.__newConnection) |
85 self.__listening = False |
74 |
86 self.__serversErrorString = "" |
75 def server(self): |
87 |
76 """ |
88 def chatWidget(self): |
77 Public method to get a reference to the server. |
89 """ |
78 |
90 Public method to get a reference to the chat widget. |
79 @return reference to the server object (CooperationServer) |
91 |
80 """ |
92 @return reference to the chat widget (ChatWidget) |
81 return self.__server |
93 """ |
|
94 return self.__chatWidget |
82 |
95 |
83 def sendMessage(self, message): |
96 def sendMessage(self, message): |
84 """ |
97 """ |
85 Public method to send a message. |
98 Public method to send a message. |
86 |
99 |
97 """ |
110 """ |
98 Public method to get the nick name. |
111 Public method to get the nick name. |
99 |
112 |
100 @return nick name (string) |
113 @return nick name (string) |
101 """ |
114 """ |
102 return "{0}@{1}:{2}".format( |
115 return "{0}@{1}@{2}".format( |
103 self.__username, |
116 self.__username, |
104 QHostInfo.localHostName(), |
117 QHostInfo.localHostName(), |
105 self.__server.serverPort() |
118 self.__servers[0].serverPort() |
106 ) |
119 ) |
107 |
120 |
108 def hasConnection(self, senderIp, senderPort=-1): |
121 def hasConnection(self, senderIp, senderPort=-1): |
109 """ |
122 """ |
110 Public method to check for an existing connection. |
123 Public method to check for an existing connection. |
166 Private slot to handle a new connection. |
179 Private slot to handle a new connection. |
167 |
180 |
168 @param connection reference to the new connection (Connection) |
181 @param connection reference to the new connection (Connection) |
169 """ |
182 """ |
170 connection.setParent(self) |
183 connection.setParent(self) |
|
184 connection.setClient(self) |
171 connection.setGreetingMessage(self.__username, |
185 connection.setGreetingMessage(self.__username, |
172 self.__server.serverPort()) |
186 self.__servers[0].serverPort()) |
173 |
187 |
174 connection.error.connect(self.__connectionError) |
188 connection.error.connect(self.__connectionError) |
175 connection.disconnected.connect(self.__disconnected) |
189 connection.disconnected.connect(self.__disconnected) |
176 connection.readyForUse.connect(self.__readyForUse) |
190 connection.readyForUse.connect(self.__readyForUse) |
177 connection.rejected.connect(self.__connectionRejected) |
191 connection.rejected.connect(self.__connectionRejected) |
252 reqConnection = self.sender() |
266 reqConnection = self.sender() |
253 participants = [] |
267 participants = [] |
254 for connectionList in self.__peers.values(): |
268 for connectionList in self.__peers.values(): |
255 for connection in connectionList: |
269 for connection in connectionList: |
256 if connection != reqConnection: |
270 if connection != reqConnection: |
257 participants.append("{0}:{1}".format( |
271 participants.append("{0}@{1}".format( |
258 connection.peerAddress().toString(), connection.serverPort())) |
272 connection.peerAddress().toString(), connection.serverPort())) |
259 reqConnection.sendParticipants(participants) |
273 reqConnection.sendParticipants(participants) |
260 |
274 |
261 def __processParticipants(self, participants): |
275 def __processParticipants(self, participants): |
262 """ |
276 """ |
263 Private slot to handle the receipt of a list of participants. |
277 Private slot to handle the receipt of a list of participants. |
264 |
278 |
265 @param participants list of participants (list of strings of "host:port") |
279 @param participants list of participants (list of strings of "host:port") |
266 """ |
280 """ |
267 for participant in participants: |
281 for participant in participants: |
268 host, port = participant.split(":") |
282 host, port = participant.split("@") |
269 port = int(port) |
283 port = int(port) |
270 |
284 |
271 if port == 0: |
285 if port == 0: |
272 msg = self.trUtf8("Illegal address: {0}:{1}\n").format(host, port) |
286 msg = self.trUtf8("Illegal address: {0}@{1}\n").format(host, port) |
273 self.connectionError.emit(msg) |
287 self.connectionError.emit(msg) |
274 else: |
288 else: |
275 if not self.hasConnection(QHostAddress(host), port): |
289 if not self.hasConnection(QHostAddress(host), port): |
276 connection = Connection(self) |
290 connection = Connection(self) |
277 self.__newConnection(connection) |
291 self.__newConnection(connection) |
295 Public method to get a list of connection given a nick name. |
309 Public method to get a list of connection given a nick name. |
296 |
310 |
297 @param nick nick name in the format of self.nickName() (string) |
311 @param nick nick name in the format of self.nickName() (string) |
298 @return list of references to the connection objects (list of Connection) |
312 @return list of references to the connection objects (list of Connection) |
299 """ |
313 """ |
300 if "@" not in nick or ":" not in nick: |
314 if "@" not in nick: |
301 # nick given in wrong format |
315 # nick given in wrong format |
302 return [] |
316 return [] |
303 |
317 |
304 user, host = nick.split(":")[0].split("@") |
318 user, host, port = nick.split("@") |
305 senderIp = QHostAddress(host) |
319 senderIp = QHostAddress(host) |
306 |
320 |
307 if senderIp not in self.__peers: |
321 if senderIp not in self.__peers: |
308 return [] |
322 return [] |
309 |
323 |
323 Public method to ban a user by its nick name. |
337 Public method to ban a user by its nick name. |
324 |
338 |
325 @param nick nick name in the format of self.nickName() (string) |
339 @param nick nick name in the format of self.nickName() (string) |
326 """ |
340 """ |
327 Preferences.syncPreferences() |
341 Preferences.syncPreferences() |
328 user = nick.split(":")[0] |
342 user = nick.split("@")[0] |
329 bannedUsers = Preferences.getCooperation("BannedUsers")[:] |
343 bannedUsers = Preferences.getCooperation("BannedUsers")[:] |
330 if user not in bannedUsers: |
344 if user not in bannedUsers: |
331 bannedUsers.append(user) |
345 bannedUsers.append(user) |
332 Preferences.setCooperation("BannedUsers", bannedUsers) |
346 Preferences.setCooperation("BannedUsers", bannedUsers) |
333 |
347 |
337 |
351 |
338 @param nick nick name in the format of self.nickName() (string) |
352 @param nick nick name in the format of self.nickName() (string) |
339 """ |
353 """ |
340 self.banUser(nick) |
354 self.banUser(nick) |
341 self.kickUser(nick) |
355 self.kickUser(nick) |
|
356 |
|
357 def startListening(self, port=-1): |
|
358 """ |
|
359 Public method to start listening for new connections. |
|
360 |
|
361 @param port port to listen on (integer) |
|
362 @return tuple giving a flag indicating success (boolean) and |
|
363 the port the server listens on |
|
364 """ |
|
365 if self.__servers: |
|
366 # do first server and determine free port |
|
367 res, port = self.__servers[0].startListening(port, True) |
|
368 if res and len(self.__servers) > 1: |
|
369 for server in self.__servers[1:]: |
|
370 res, port = server.startListening(port, False) |
|
371 if not res: |
|
372 self.__serversErrorString = server.errorString() |
|
373 else: |
|
374 self.__serversErrorString = self.__servers[0].errorString() |
|
375 else: |
|
376 res = False |
|
377 self.__serversErrorString = self.trUtf8("No servers present.") |
|
378 |
|
379 if res: |
|
380 self.__serversErrorString = "" |
|
381 self.__listening = res |
|
382 return res, port |
|
383 |
|
384 def isListening(self): |
|
385 """ |
|
386 Public method to check, if the client is listening for connections. |
|
387 |
|
388 @return flag indicating the listening state (boolean) |
|
389 """ |
|
390 return self.__listening |
|
391 |
|
392 def close(self): |
|
393 """ |
|
394 Public method to close all connections and stop listening. |
|
395 """ |
|
396 for server in self.__servers: |
|
397 server.close() |
|
398 self.__listening = False |
|
399 |
|
400 def errorString(self): |
|
401 """ |
|
402 Public method to get a human readable error message about the last server error. |
|
403 |
|
404 @return human readable error message about the last server error (string) |
|
405 """ |
|
406 return self.__serversErrorString |