-
Notifications
You must be signed in to change notification settings - Fork 5
/
IncomingPackets.py
316 lines (280 loc) · 10.6 KB
/
IncomingPackets.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
#(C) Marek Chrusciel,
# Jakub Kosinski,
# Marcin Krupowicz,
# Mateusz Strycharski
#
# $Id$
import struct
from Contacts import *
from Helpers import Enum
GGIncomingPackets = Enum({
"GGWelcome":0x0001, #Poczatek komunikacji z serwerem - przychodzi seed potrzebny do zalogowania sie
"GGStatus": 0x0002, #Zmiana stanu przed GG 6.0
"GGLoginOK":0x0003, #Logowanie sie powiodlo
"GGSendMsgAck":0x0005, #Potwierdzenie wiadomosci
"GGPong":0x0007, #Pong
"GGLoginFailed":0x0009, #Logowanie sie nie powiodlo
"GGRecvMsg":0x000a, # Przyszla nowa wiadomosc
"GGDisconnecting":0x000b, #Zerwanie polaczenia
"GGNotifyReplyOld":0x000c, #Stan listy kontaktow przed GG 6.0
"GGPubDir50Reply":0x000e, #Odpowiedz katalogu publicznego
"GGUserListReply":0x0010, #Odpowiedz listy kontaktow na serwerze
"GGNotifyReply60":0x0011, #Stan listy kontaktow
"GGNeedEMail":0x0014, #Logowanie powiodlo sie, ale powinnismy uzupelnic adres e-mail w katalogu publicznym
"GGNotifyReply77":0x0018, # Stan listy kontaktow (GG 7.0)
"GGStatus":0x0002, # Zmiana statusu uzytkownika z listy kontaktow
"GGStatus60":0x000f # jw. (wersja > 6)
})
class GGIncomingPacket(object):
"""
"Abstrakcyjna" klasa pakietow przychodzacych od serwera
"""
def read(self, connection, size):
pass
class GGWelcome(GGIncomingPacket):
"""
Pakiet wysylany przez serwer zaraz po nawiazaniu polaczenia.
Znajduje sie w nim 'seed' konieczny do zalogowania sie do serwera.
"""
def __init__(self):
self.seed = None
def read(self, connection, size):
self.seed = struct.unpack("<I", connection.read(size))[0]
class GGLoginOK(GGIncomingPacket):
"""
Pakiety wysylany przez serwer w przypadku pomyslnego zalogowania sie.
Czasem ma dane dlugosci 1 (0x1f), a czasem nic nie ma...
"""
def __init__(self):
pass
def read(self, connection, size):
connection.read(size)
class GGRecvMsg(GGIncomingPacket):
"""
Pakiet przychodzacej wiadomosci. Jego struktura jest nastepujaca:
int sender -- numer nadawcy
int seq -- numer sekwencyjny
int time -- czas nadania
int class -- klasa wiadomosci
string -- tresc wiadomosci
"""
def __init__(self):
pass
def read(self, connection, size):
structure = struct.unpack("<IIII%ds" % (size - 16), connection.read(size))
self.sender = structure[0]
self.seq = structure[1]
self.time = structure[2]
self.msg_class = structure[3]
self.message = structure[4].split("\x00")[0] #czasem dziwne smici sa doklejane. Chyba tylko w najnowszej wersji GG. NIe zmienia to faktu, ze zawsze po wiadomosci
#jest 0x00. Mozemy wiec odciac koncowke - obojetnie co w niej jest. TODO: co jest w tej koncowce? :?
#O, to jest chyba formatowanie. Ucinamy to na razie
class GGSendMsgAck(GGIncomingPacket):
"""
Pakiet ten serwer do nas przesyla zaraz po wyslaniu wiadomosci. Jego struktura jest nastepujaca:
int status -- stan wiadomosci
int recipient -- numer odbiorcy
int seq -- numer sekwencyjny
"""
def __init__(self):
pass
def read(self, connection, size):
structure = struct.unpack("<III", connection.read(size))
self.status = structure[0]
self.recipient = structure[1]
self.seq = structure[2]
class GGNotifyReplyOld(GGIncomingPacket):
"""
___Pakiet dla starych wersji klientow___
Odpowiedz serwera na pakiety GGNotifyFirst i GGNotifyLast.
Zawiera liste struktur postaci:
int uin -- numer
char status -- status danej osoby
int remote_ip -- adres ip delikwenta
short remote_port -- port, na ktorym slucha klient
int version -- wersja klienta
string description -- opis, nie musi wystapic
int time -- czas, nie musi wystapic
"""
def __init__(self, contacts):
assert type(contacts) == ContactsList
self.__contacts = contacts
def read(self, connection, size):
raise NotImplemented
def __get_contacts(self):
return self.__contacts
contacts = property(__get_contacts)
class GGNotifyReply(GGIncomingPacket):
"""
Odpowiedz serwera na pakiety GGNotifyFirst i GGNotifyLast.
Zawiera liste struktur postaci:
int uin -- numer
char status -- status danej osoby
int ip -- adres ip osoby
short port -- port, na ktorym slucha klient
int version -- wersja klienta
string description -- opis, nie musi wystapic
int return_time -- czas, nie musi wystapic
"""
def __init__(self, contacts, notify_reply_version = GGIncomingPackets.GGNotifyReply60):
"""
Domyslnie odbieramy pakiet starszy - GGNotifyReply60
"""
assert type(contacts) == ContactsList
assert notify_reply_version == GGIncomingPackets.GGNotifyReply60 or notify_reply_version == GGIncomingPackets.GGNotifyReply77
self.notify_reply_version = notify_reply_version
if contacts == None:
self.__contacts = ContactsList()
else:
self.__contacts = contacts
def read(self, connection, size):
if size != 0:
dummy_size = (self.notify_reply_version == GGIncomingPackets.GGNotifyReply60 and 1 or 4)
count = 0 #ile juz odebralismy bajtow
finish = False #czy juz konczymy odbieranie
while not finish:
tuple = struct.unpack("<IBIHBB%dx" % (dummy_size,), connection.read(13 + dummy_size))
count += 13 + dummy_size
status = tuple[1]
uin = (tuple[0] & 0x00ffffff)#bierzemy UIN, maske odrzucamy
if self.__contacts[uin] == None:
self.__contacts.add_contact(Contact({'uin':uin}))
self.__contacts[uin].uin = uin
self.__contacts[uin].status = tuple[1]
self.__contacts[uin].ip = tuple[2]
self.__contacts[uin].port = tuple[3]
self.__contacts[uin].version = tuple[4]
self.__contacts[uin].image_size = tuple[5]
#czy status jest opisowy? Jesli nie, to znaczy, ze dalej zaczyna sie info o kolejnym numerku
if status == GGStatuses.AvailDescr or status == GGStatuses.NotAvailDescr or status == GGStatuses.BusyDescr or status == GGStatuses.InvisibleDescr:
# zostala jeszcze na pewno dlugosc opisu i opis (moze tez czas)
tuple = struct.unpack("<B", connection.read(1))
count += 1
desc_size = tuple[0]
if desc_size <=4:
tuple = struct.unpack("<%ds" % (desc_size,), connection.read(desc_size))
self.__contacts[uin].description = tuple[0]
count += desc_size
else:
tuple = struct.unpack("<%ds" % ((desc_size - 4),), connection.read(desc_size - 4)) #bo zaraz sprawdzimy czy ostatnim bajtem w tuple[0] jest 0x00.
count += desc_size - 4
#jesli tak, to znaczy, ze na koncu jest czas. Jesli nie, to znaczy, ze
#dalsze 4 bajty, to dalsza czesc opisu
description = tuple[0]
if ord(description[len(description)-1]) == 0x00: # 4 kolejne bajty, to czas
description.replace(chr(0x00), '') #usuwamy 0x00
tuple = struct.unpack("<I", connection.read(4))
count += 4
self.__contacts[uin].description = description
self.__contacts[uin].return_time = tuple[0]
else: #4 kolejne bajty, to koncowka opisu
tuple = struct.unpack("4s", connection.read(4))
count += 4
description += tuple[0]
self.__contacts[uin].description = description
self.__contacts[uin].return_time = 0
if count >= size:
finish = True
def __get_contacts(self):
return self.__contacts
contacts = property(__get_contacts)
class GGPubDir50Reply(GGIncomingPacket):
"""
Odpowiedz serwera na pakiet GGPubDir50Request o nastepujacej strukturze:
char reqtype -- typ odpowiedzi
int seq -- numer sekwencyjny
char[] reply -- wyniki wyszukiwania w formacie "parametr\0wartosc\0", poszczegolne osoby oddzielone sa pustym polem ("\0")
Przyklad odpowiedzi zawierajacej dwie osoby (znaki "\0" zamienione zostaly na znak "."):
FmNumber.12345.FmStatus.1.firstname.Adam.nickname.Janek.birthyear.1979.city.Wzdow..FmNumber.32123.FmStatus.5.
firstname.Ewa.nickname.Ewcia.birthyear.1982.city.Gdansk..nextstart.0.
Odpowiedz katalogu nie zawiera nazwisk oraz plci znalezionych osob.
"""
def __init__(self):
pass
def read(self, connection, size):
structure = struct.unpack("<BI%ds" % (size - 5), connection.read(size))
self.reqtype = structure[0]
self.seq = structure[1]
self.reply = structure[2]
class GGDisconnecting(GGIncomingPacket):
"""
Pusty pakiet, ktory serwer wysyla, gdy chce nas rozlaczyc. Ma to miejsce,
gdy probowano polaczyc sie z nieprawidlowym haslem lub gdy rownoczesnie
polaczy sie drugi klient z tym samym numerem
"""
def __init__(self):
pass
def read(self, connection, size):
connection.read(size)
class GGUserListReply(GGIncomingPacket):
"""
Odpowiedz serwera na pakiet GGUserListRequest
TODO: poprawki, bo ta metoda nie dziala dobrze (w przypadku, gdy request jest pusty, a moze sie tak zdarzyc)
"""
def __init__(self):
pass
def read(self, connection, size):
if size == 1:
self.reqtype = struct.unpack("<B", connection.read(size))[0]
self.request = ""
else:
structure = struct.unpack("<B%ds" % (size - 1), connection.read(size))
self.reqtype = structure[0]
self.request = structure[1]
class GGStatus(GGIncomingPacket):
"""
Pakiet informujacy o zmianie statusu uzytkownika na liscie kontaktow
"""
def __init__(self):
pass
def read(self, connection, size):
self.return_time = 0
if size == 8:
structure = struct.unpack("<II", connection.read(size))
self.uin = structure[0] & 0x00ffffff # TODO: to moze byc niepotrzebne
self.status = structure[1]
self.description = ""
else:
structure = struct.unpack("<II%ds" % (size - 8), connection.read(size))
self.uin = structure[0] & 0x00ffffff
self.status = structure[1]
self.description = structure[2]
if len(self.description) <= 4:
pass
elif ord(structure[2][-5]) == 0:
tuple = struct.unpack("<%dsxI" % (len(self.description) - 5), self.description)
self.description = tuple[0]
self.return_time = tuple[1]
class GGStatus60(GGIncomingPacket):
"""
Pakiet informujacy o zmianie statusu uzytkownika na liscie kontaktow (dla wersji klienta > 6.0)
"""
def __init__(self):
pass
def read(self, connection, size):
if size == 14:
structure = struct.unpack("<IBIHBBx", connection.read(size))
self.uin = structure[0] & 0x00ffffff
self.status = structure[1]
self.ip = structure[2]
self.port = structure[3]
self.version = structure[4]
self.image_size = structure[5]
self.description = ""
self.return_time = 0
else:
structure = struct.unpack("<IBIHBBx%ds" % (size - 14), connection.read(size))
self.uin = structure[0] & 0x00ffffff
self.status = structure[1]
self.ip = structure[2]
self.port = structure[3]
self.version = structure[4]
self.image_size = structure[5]
self.description = structure[6]
self.return_time = 0
#if len(self.description) <= 4:
# pass
if ord(structure[6][-5]) == 0:
tuple = struct.unpack("<%dsxI" % (len(self.description) - 5), self.description)
self.description = tuple[0]
self.return_time = tuple[1]