-
Notifications
You must be signed in to change notification settings - Fork 5
/
Helpers.py
306 lines (265 loc) · 8.96 KB
/
Helpers.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
#(C) Marek Chrusciel,
# Jakub Kosinski,
# Marcin Krupowicz,
# Mateusz Strycharski
#
# $Id$
import types
import socket
import struct
import hashlib
import re
#============================
# Enum
#
class Enum(object):
"""
Klasa reprezentujaca typ wyliczeniowy.
Uzycie (np.):
IncomingPackets = Enum({"GGRecvMsg":0x000a, "GGWelcome":0x0001})
"""
def __init__(self, enums = {}):
self.__lookup = enums
self.__reverse_lookup = {}
for k, v in self.__lookup.iteritems():
self.__reverse_lookup[v] = k
def __getattr__(self, key):
"""
Funkcja ta pozwala nam korzystac z klasy w taki sposob (odnosnie przykladu z opisu klasy):
if packet_type == IncomingPackets.GGRecvMsg: (...)
Returns: wartosc elementu 'key'
"""
if not self.__lookup.has_key(key):
raise AttributeError
return self.__lookup[key]
def reverse_lookup(self, value):
"""
Funkcja pozwala na sprawdzenie odwrotnej wartosc, czyli np.:
IncomingPackets.reverse_lookup(0x000a) - zwroci "GGRecvMsg"
Returns: klucz dla ktorego wartoscia jest 'value'
"""
attributes = []
for x in self.__reverse_lookup.keys():
if int(x) & int(value):
attributes.append(self.__reverse_lookup[x])
return ",".join(attributes)
def reverse_lookup_without_mask(self,value):
if not self.__reverse_lookup.has_key(value):
raise AttributeError(value)
return self.__reverse_lookup[value]
def __contains__(self, value):
"""
Sprawdza, czy wartosc znajduje sie w Enumie, np.:
if 0x002 in GGStatuses:
[...]
"""
return self.__reverse_lookup.has_key(value)
#
# Enum
#========================
#============================
# Obsluga zdarzen
#
class EventArgs(object):
"""
Klasa argumentow przekazywanych do zdarzen. Sposob wykorzystania (przyklad):
* self.on_status_changed(EventArgs({"uin":uin, "status":status})) - w pygglib.py (__events_loop)
* def status_changed_event_handler(sender, args): - w programie korzystajacym z biblioteki (podpiete zdarzenie)
print 'Uzytkownik %d zmienil status na %d' % (args.uin, args.status)
"""
def __init__(self, args):
"""
args - slownik, ktorego kluczami sa nazwy argumentow, a wartosciami - wartosci dla podanych argumentow.
np.: args = { "uin":3993939, name:"Jasio", ip:"127.0.0.1" }
"""
self.__args = args
def __getattr__(self, arg):
"""
Pobiera wartosc argumentu o nazwie arg
"""
if self.__args.has_key(arg):
return self.__args[arg]
else:
raise AttributeError
def args(self):
return self.__args.keys()
class UnknowEventError(AttributeError):
pass
class UnknowEventHandlerError(AttributeError):
pass
class NotCallableError(Exception):
pass
class Event(object):
""" Stanowi liste funkcji, ktora mozna wywolac za pomoca: __call__(*args), czyli np.:
on_msg_recv = Event([f1, f2, f3])
on_msg_recv() - uruchomi wszytkie funkcje: f1, f2, f2
"""
def __init__(self, funs):
for f in funs:
if not callable(f):
raise NotCallableError
self.__funs = funs
def __call__(self, *args):
for f in self.__funs:
apply(f, args)
class EventsList(object):
def __init__(self, events):
self.__events = {}
self.__event_handlers = {} # slownik odwrotny do slownika __events (potrzebne do unregister)
for e in events:
self.__events[e] = [] # kazde zdarzenie ma na poczatku pusta liste funkcji
self.__slots__ = events # innych nie mozna wywolac! Tylko te, ktore na poczatku podalismy
def __getattr__(self, event):
""" Returns: liste funkcji ktore obsluguja zdarzenie 'event'
"""
if not self.__events.has_key(event):
raise AttributeError("event: %s", event)
return Event(self.__events[event])
def register(self, event, event_handler):
if not self.__events.has_key(event):
raise UnknowEventError
if not callable(event_handler):
raise NotCallableError
self.__events[event].append(event_handler)
self.__event_handlers[event_handler] = event
def unregister(self, event, event_handler):
if not self.__event_handlers.has_key(event_handler):
raise UnknowEventHandler
del self.__event_handlers[event_handler] # usuwamy handlera ze slownika odwrotnego
del self.__events[event][self._events[event].index(event_handler)] # i usuwamy handlera z listy funkcji zdarzeia
#
# Oblsuga zdarzen
#========================
def gg_login_hash(password, seed):
assert type(password) == types.StringType
#assert type(seed) == types.IntType
x = 0L
y = long(seed)
z = 0L
for c in password:
x = (x & 0xffffffffL) | ord(c)
y ^= x
y &= 0xffffffffL
y += x
y &= 0xffffffffL
x <<= 8
x &= 0xffffffffL
y ^= x
y &= 0xffffffffL
x <<= 8
x &= 0xffffffffL
y -= x
y &= 0xffffffffL
x <<= 8
x &= 0xffffffffL
y ^= x
y &= 0xffffffffL
z = y & 0x1f
y = (y << z) | (y >> (32 - z))
y &= 0xffffffffL
return y
#return struct.pack("<I60s", y, str(0x00))[0]
#return hashlib.sha1(password).digest()
def gg_http_hash(email,pwd):
"""
Zwraca hash z emaila oraz hasla, potrzebny przy rejestracji i usuwaniu konta
"""
a = 0
b = -1
for c in email:
a = (ord(c) ^ b) + ((ord(c) << 8) & 0xffffffff);
b = (a >> 24) | ((a << 8) & 0xffffffff);
for c in pwd:
a = (ord(c) ^ b) + ((ord(c) << 8) & 0xffffffff);
b = (a >> 24) | ((a << 8) & 0xffffffff);
return int(abs(b))
def ip_to_int32(ip):
assert type(ip) == types.StringType
return struct.unpack("<I", socket.inet_aton(ip))[0]
def split_list(xs, size):
"""
Dzieli liste 'xs' na podlisty. Kazda z nich, oprocz ostatniej, ma dlugosc 'size'.
Ostatnia podlista ma dlugosc <= 'size'.
Returns: liste podlist
"""
import math
ret = []
ret_size = int(math.ceil(len(xs)/float(size)))
for i in range(ret_size):
ret.append(xs[(i*size):((i+1)*size)])
return ret
def dict_to_request(hash):
"""
Zamienia slownik parametrow zapytania do katalogu publicznego na format rozpoznawany przez serwer Gadu-Gadu
"""
assert type(hash) == types.DictType
request = ""
for x in hash:
request += str(x) + "\0" + str(hash[x]) + "\0"
return request
def request_to_dict(request):
"""
Zamienia parametry zapytania do katalogu publicznego rozpoznawane przez serwer Gadu-Gadu na slownik postaci nazwa_parametru:wartosc
"""
assert type(request) == types.StringType
list = request.split("\0")
tuples = []
i = 0
while i < len(list) - 1:
tuples.append((list[i],list[i+1]))
i += 2
return dict(tuples)
def pygglib_rtf_to_gg_rtf(rtf_msg):
"""
Konwertuje tekst z formatu pygglib richtext do formatu Gadu-Gadu richtext.
pygglib richtext: <b>Ala <i><u>ma</u></i></b><color red=123 green=143 blue=123> KOTA</color>
Gadu-Gadu richtext: /patrz opis protokolu Gadu-Gadu (http://ekg.chmurka.net/docs/protocol.html#ch1.6)
"""
plain_text = "" #czysty tekst, bez formatowania (GG richtext zaczyna sie od czystego tekstu)
format_string = "" #ciag formatujacy plain_text (patrz. opis protokolu)
regexp = re.compile(r'<(/?(color|i|b|u)[^>]*)>')
colors_regexp = re.compile(r'color\s+red=(?P<red>[0-9]{1,3})\s+green=(?P<green>[0-9]{1,3})\s+blue=(?P<blue>[0-9]{1,3})')
markups_length = 0 #laczna dlugosc wszystkich znacznikow (potrzebne do oznaczania pozycji formatowanego tekstu)
flags = 0x0
for format in regexp.finditer(rtf_msg):
markup = format.groups(0)[0]
markups_length += len(markup) + 2
colors_match = colors_regexp.match(markup)
if markup == 'b':
format_string += struct.pack('<HB', format.end(0) - markups_length, flags | 0x01)
elif markup == 'i':
format_string += struct.pack('<HB', format.end(0) - markups_length, flags | 0x02)
elif markup == 'u':
format_string += struct.pack('<HB', format.end(0) - markups_length, flags | 0x04)
elif colors_match is not None:
red = int(colors_match.group('red'))
green = int(colors_match.group('green'))
blue = int(colors_match.group('blue'))
format_string += struct.pack('<HBBBB', format.end(0) - markups_length, flags | 0x08, red, green, blue)
elif markup == '/b':
format_string += struct.pack('<HB', format.end(0) - markups_length, flags ^ 0x01)
elif markup == '/i':
format_string += struct.pack('<HB', format.end(0) - markups_length, flags ^ 0x02)
elif markup == '/u':
format_string += struct.pack('<HB', format.end(0) - markups_length, flags ^ 0x04)
elif markup == '/color':
format_string += struct.pack('<HB', format.end(0) - markups_length, flags ^ 0x08)
else:
pass
#TODO: cos nie tak jest :(
plain_text = re.sub(r'</?(color|i|b|u)[^>]*>', '', rtf_msg) #usuwamy znaczniki formatowania
return struct.pack('<%dsBH%ds' % (len(plain_text) + 1, len(format_string)), plain_text, 0x02, len(format_string), format_string) #TODO: +1?????
def gg_rtf_to_pygglib_rtf(rtf_msg):
"""
Konwertuje tekst z formatu Gadu-Gadu richtext do formatu pygglib richtext.
pygglib richtext: <b>Ala <i><u>ma</u></i></b><color red=123 green=143 blue=123> KOTA</color>
Gadu-Gadu richtext: /patrz opis protokolu Gadu-Gadu (http://ekg.chmurka.net/docs/protocol.html#ch1.6)
"""
try:
index = rtf_msg.index('\x02')
except ValueError:
return rtf_msg
plain_text, format_string = rtf_msg[:index], rtf_msg[index:]
#TODO: skonczyc....
#if __name__ == "__main__":
# pygglib_rtf_to_gg_rtf("<b>Ala <i>ma</i></b><color red=123 green=143 blue=123> KOTA</color>")