|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2015 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing a class to read flash cookies. |
|
8 """ |
|
9 |
|
10 # |
|
11 # Note: The code is based on s2x.py |
|
12 # |
|
13 |
|
14 from __future__ import unicode_literals |
|
15 |
|
16 import struct |
|
17 import io |
|
18 |
|
19 from PyQt5.QtCore import QDateTime |
|
20 |
|
21 |
|
22 class FlashCookieReaderError(Exception): |
|
23 """ |
|
24 Class containing data of a reader error. |
|
25 """ |
|
26 def __init__(self, msg): |
|
27 """ |
|
28 Constructor |
|
29 |
|
30 @param msg error message |
|
31 @type str |
|
32 """ |
|
33 self.msg = msg |
|
34 |
|
35 |
|
36 class FlashCookieReader(object): |
|
37 """ |
|
38 Class implementing a reader for flash cookies (*.sol files). |
|
39 """ |
|
40 Number = b'\x00' |
|
41 Boolean = b'\x01' |
|
42 String = b'\x02' |
|
43 ObjObj = b'\x03' |
|
44 Null = b'\x05' |
|
45 Undef = b'\x06' |
|
46 ObjArr = b'\x08' |
|
47 ObjDate = b'\x0B' |
|
48 ObjM = b'\x0D' |
|
49 ObjXml = b'\x0F' |
|
50 ObjCc = b'\x10' |
|
51 |
|
52 EpochCorrectionMsecs = 31 * 24 * 60 * 60 * 1000 |
|
53 # Flash Epoch starts at 1969-12-01 |
|
54 |
|
55 def __init__(self): |
|
56 """ |
|
57 Constructor |
|
58 """ |
|
59 self.__result = {} |
|
60 # dictionary with element name as key and tuple of |
|
61 # type and value as value |
|
62 self.__data = None |
|
63 self.__parsed = False |
|
64 |
|
65 def setBytes(self, solData): |
|
66 """ |
|
67 Public method to set the contents of a sol file to be parsed. |
|
68 |
|
69 @param solData contents of the file |
|
70 @type bytes |
|
71 """ |
|
72 self.__data = io.BytesIO(solData) |
|
73 |
|
74 def setFileName(self, solFilename): |
|
75 """ |
|
76 Public method to set the name of a sol file to be parsed. |
|
77 |
|
78 @param solFilename name of the sol file |
|
79 @type str |
|
80 """ |
|
81 self.__data = open(solFilename, "rb") |
|
82 |
|
83 def setFile(self, solFile): |
|
84 """ |
|
85 Public method to set an open sol file to be parsed. |
|
86 |
|
87 @param solFile sol file to be parsed |
|
88 @type io.FileIO |
|
89 """ |
|
90 self.__data = solFile |
|
91 |
|
92 def parse(self): |
|
93 """ |
|
94 Public method to parse the sol file. |
|
95 |
|
96 @exception FlashCookieReaderError raised when encountering a parse |
|
97 issue |
|
98 """ |
|
99 if self.__data is None: |
|
100 return |
|
101 |
|
102 self.__data.seek(0, 2) |
|
103 lenSolData = self.__data.tell() |
|
104 self.__data.seek(0) |
|
105 self.__data.read(2) |
|
106 sLenData = self.__data.read(4) |
|
107 lenData, = struct.unpack(">L", sLenData) # unsigned long, big-endian |
|
108 if lenSolData != lenData + 6: |
|
109 print("Warning: data length doesn't match.") |
|
110 sDataType = self.__data.read(4).decode("utf-8") # 'TCSO' |
|
111 if sDataType != "TCSO": |
|
112 raise FlashCookieReaderError( |
|
113 "Flash cookie type is not 'TCSO'; found '{0}'." |
|
114 .format(sDataType)) |
|
115 self.__data.read(6) |
|
116 lenSolName, = struct.unpack(">H", self.__data.read(2)) |
|
117 # unsigned short, big-endian |
|
118 solName = self.__data.read(lenSolName) |
|
119 solName = solName.decode("utf-8") |
|
120 self.__result["SolName"] = ("string", solName) |
|
121 self.__data.read(4) |
|
122 while self.__data.tell() < lenSolData: |
|
123 lenVariableName, = struct.unpack(">H", self.__data.read(2)) |
|
124 # unsigned short, big-endian |
|
125 variableName = self.__data.read(lenVariableName) |
|
126 variableName = variableName.decode("utf-8") |
|
127 variableType = self.__data.read(1) |
|
128 if variableType == self.Number: |
|
129 self.__parseNumber(variableName, self.__result) |
|
130 elif variableType == self.Boolean: |
|
131 self.__parseBoolean(variableName, self.__result) |
|
132 elif variableType == self.String: |
|
133 self.__parseString(variableName, self.__result) |
|
134 elif variableType == self.ObjObj: |
|
135 self.__parseObject(variableName, self.__result) |
|
136 elif variableType == self.ObjArr: |
|
137 self.__parseArray(variableName, self.__result) |
|
138 elif variableType == self.ObjDate: |
|
139 self.__parseDate(variableName, self.__result) |
|
140 elif variableType == self.ObjXml: |
|
141 self.__parseXml(variableName, self.__result) |
|
142 elif variableType == self.ObjCc: |
|
143 self.__parseOcc(variableName, self.__result) |
|
144 elif variableType == self.ObjM: |
|
145 self.__parseOjm(variableName, self.__result) |
|
146 elif variableType == self.Null: |
|
147 self.__parseNull(variableName, self.__result) |
|
148 elif variableType == self.Undef: |
|
149 self.__parseUndefined(variableName, self.__result) |
|
150 else: |
|
151 raise FlashCookieReaderError( |
|
152 "Unexpected Data Type: " + hex(ord(variableType))) |
|
153 self.__data.read(1) # '\x00' |
|
154 self.__data.close() |
|
155 self.__parsed = True |
|
156 |
|
157 def __parseNumber(self, variableName, parent): |
|
158 """ |
|
159 Private method to parse a number. |
|
160 |
|
161 @param variableName name of the variable to be parsed |
|
162 @type str |
|
163 @param parent reference to the dictionary to insert the result into |
|
164 @type dict |
|
165 """ |
|
166 b = self.__data.read(8) |
|
167 if b == b"\x7F\xF0\x00\x00\x00\x00\x00\x00": |
|
168 value = "Infinity" |
|
169 elif b == b"\xFF\xF0\x00\x00\x00\x00\x00\x00": |
|
170 value = "-Infinity" |
|
171 elif value == b"\x7F\xF8\x00\x00\x00\x00\x00\x00": |
|
172 value = "NaN" |
|
173 else: |
|
174 value, = struct.unpack(">d", b) # double, big-endian |
|
175 value = str(value) |
|
176 parent[variableName] = ("number", value) |
|
177 |
|
178 def __parseBoolean(self, variableName, parent): |
|
179 """ |
|
180 Private method to parse a boolean. |
|
181 |
|
182 @param variableName name of the variable to be parsed |
|
183 @type str |
|
184 @param parent reference to the dictionary to insert the result into |
|
185 @type dict |
|
186 """ |
|
187 b = self.__data.read(1) |
|
188 if b == b"\x00": |
|
189 value = "False" |
|
190 elif b == b"\x01": |
|
191 value = "True" |
|
192 else: |
|
193 # boolean value error; default to True |
|
194 value = "True" |
|
195 parent[variableName] = ("boolean", value) |
|
196 |
|
197 def __parseString(self, variableName, parent): |
|
198 """ |
|
199 Private method to parse a string. |
|
200 |
|
201 @param variableName name of the variable to be parsed |
|
202 @type str |
|
203 @param parent reference to the dictionary to insert the result into |
|
204 @type dict |
|
205 """ |
|
206 lenStr, = struct.unpack(">H", self.__data.read(2)) |
|
207 # unsigned short, big-endian |
|
208 b = self.__data.read(lenStr) |
|
209 value = b.decode("utf-8") |
|
210 parent[variableName] = ("string", value) |
|
211 |
|
212 def __parseDate(self, variableName, parent): |
|
213 """ |
|
214 Private method to parse a date. |
|
215 |
|
216 @param variableName name of the variable to be parsed |
|
217 @type str |
|
218 @param parent reference to the dictionary to insert the result into |
|
219 @type dict |
|
220 """ |
|
221 msec, = struct.unpack(">d", self.__data.read(8)) |
|
222 # double, big-endian |
|
223 # DateObject: Milliseconds Count From Dec. 1, 1969 |
|
224 msec -= self.EpochCorrectionMsecs # correct for Unix epoch |
|
225 minOffset, = struct.unpack(">h", self.__data.read(2)) |
|
226 # short, big-endian |
|
227 offset = minOffset // 60 # offset in hours |
|
228 # Timezone: UTC + Offset |
|
229 value = QDateTime() |
|
230 value.setMSecsSinceEpoch(msec) |
|
231 value.setOffsetFromUtc(offset * 3600) |
|
232 parent[variableName] = ("date", |
|
233 value.toString("yyyy-MM-dd HH:mm:ss t")) |
|
234 |
|
235 def __parseXml(self, variableName, parent): |
|
236 """ |
|
237 Private method to parse XML. |
|
238 |
|
239 @param variableName name of the variable to be parsed |
|
240 @type str |
|
241 @param parent reference to the dictionary to insert the result into |
|
242 @type dict |
|
243 """ |
|
244 lenCData, = struct.unpack(">L", self.__data.read(4)) |
|
245 # unsigned long, big-endian |
|
246 cData = self.__data.read(lenCData) |
|
247 value = cData.decode("utf-8") |
|
248 parent[variableName] = ("xml", value) |
|
249 |
|
250 def __parseOjm(self, variableName, parent): |
|
251 """ |
|
252 Private method to parse an m_object. |
|
253 |
|
254 @param variableName name of the variable to be parsed |
|
255 @type str |
|
256 @param parent reference to the dictionary to insert the result into |
|
257 @type dict |
|
258 """ |
|
259 parent[variableName] = ("m_object", "") |
|
260 |
|
261 def __parseNull(self, variableName, parent): |
|
262 """ |
|
263 Private method to parse a null object. |
|
264 |
|
265 @param variableName name of the variable to be parsed |
|
266 @type str |
|
267 @param parent reference to the dictionary to insert the result into |
|
268 @type dict |
|
269 """ |
|
270 parent[variableName] = ("null", "") |
|
271 |
|
272 def __parseUndefined(self, variableName, parent): |
|
273 """ |
|
274 Private method to parse an undefined object. |
|
275 |
|
276 @param variableName name of the variable to be parsed |
|
277 @type str |
|
278 @param parent reference to the dictionary to insert the result into |
|
279 @type dict |
|
280 """ |
|
281 parent[variableName] = ("undefined", "") |
|
282 |
|
283 def __parseObject(self, variableName, parent): |
|
284 """ |
|
285 Private method to parse an object. |
|
286 |
|
287 @param variableName name of the variable to be parsed |
|
288 @type str |
|
289 @param parent reference to the dictionary to insert the result into |
|
290 @type dict |
|
291 """ |
|
292 value = {} |
|
293 parent[variableName] = ("object", value) |
|
294 |
|
295 lenVariableName, = struct.unpack(">H", self.__data.read(2)) |
|
296 # unsigned short, big-endian |
|
297 while lenVariableName != 0: |
|
298 variableName = self.__data.read(lenVariableName) |
|
299 variableName = variableName.decode("utf-8") |
|
300 variableType = self.__data.read(1) |
|
301 if variableType == self.Number: |
|
302 self.__parseNumber(variableName, value) |
|
303 elif variableType == self.Boolean: |
|
304 self.__parseBoolean(variableName, value) |
|
305 elif variableType == self.String: |
|
306 self.__parseString(variableName, value) |
|
307 elif variableType == self.ObjObj: |
|
308 self.__parseObject(variableName, value) |
|
309 elif variableType == self.ObjArr: |
|
310 self.__parseArray(variableName, value) |
|
311 elif variableType == self.ObjDate: |
|
312 self.__parseDate(variableName, value) |
|
313 elif variableType == self.ObjXml: |
|
314 self.__parseXml(variableName, value) |
|
315 elif variableType == self.ObjCc: |
|
316 self.__parseOcc(variableName, value) |
|
317 elif variableType == self.ObjM: |
|
318 self.__parseOjm(variableName, value) |
|
319 elif variableType == self.Null: |
|
320 self.__parseNull(variableName, value) |
|
321 elif variableType == self.Undef: |
|
322 self.__parseUndefined(variableName, value) |
|
323 else: |
|
324 raise FlashCookieReaderError( |
|
325 "Unexpected Data Type: " + hex(ord(variableType))) |
|
326 lenVariableName, = struct.unpack(">H", self.__data.read(2)) |
|
327 self.__data.read(1) # '\x09' |
|
328 |
|
329 def __parseArray(self, variableName, parent): |
|
330 """ |
|
331 Private method to parse an array. |
|
332 |
|
333 @param variableName name of the variable to be parsed |
|
334 @type str |
|
335 @param parent reference to the dictionary to insert the result into |
|
336 @type dict |
|
337 """ |
|
338 arrayLength, = struct.unpack(">L", self.__data.read(4)) |
|
339 # unsigned long, big-endian |
|
340 |
|
341 value = {} |
|
342 parent[variableName] = ("array; length={0}".format(arrayLength), value) |
|
343 |
|
344 lenVariableName, = struct.unpack(">H", self.__data.read(2)) |
|
345 # unsigned short, big-endian |
|
346 while lenVariableName != 0: |
|
347 variableName = self.__data.read(lenVariableName) |
|
348 variableName = variableName.decode("utf-8") |
|
349 variableType = self.__data.read(1) |
|
350 if variableType == self.Number: |
|
351 self.__parseNumber(variableName, value) |
|
352 elif variableType == self.Boolean: |
|
353 self.__parseBoolean(variableName, value) |
|
354 elif variableType == self.String: |
|
355 self.__parseString(variableName, value) |
|
356 elif variableType == self.ObjObj: |
|
357 self.__parseObject(variableName, value) |
|
358 elif variableType == self.ObjArr: |
|
359 self.__parseArray(variableName, value) |
|
360 elif variableType == self.ObjDate: |
|
361 self.__parseDate(variableName, value) |
|
362 elif variableType == self.ObjXml: |
|
363 self.__parseXml(variableName, value) |
|
364 elif variableType == self.ObjCc: |
|
365 self.__parseOcc(variableName, value) |
|
366 elif variableType == self.ObjM: |
|
367 self.__parseOjm(variableName, value) |
|
368 elif variableType == self.Null: |
|
369 self.__parseNull(variableName, value) |
|
370 elif variableType == self.Undef: |
|
371 self.__parseUndefined(variableName, value) |
|
372 else: |
|
373 raise FlashCookieReaderError( |
|
374 "Unexpected Data Type: " + hex(ord(variableType))) |
|
375 lenVariableName, = struct.unpack(">H", self.__data.read(2)) |
|
376 self.__data.read(1) # '\x09' |
|
377 |
|
378 def __parseOcc(self, variableName, parent): |
|
379 """ |
|
380 Private method to parse a c_object. |
|
381 |
|
382 @param variableName name of the variable to be parsed |
|
383 @type str |
|
384 @param parent reference to the dictionary to insert the result into |
|
385 @type dict |
|
386 """ |
|
387 lenCname = struct.unpack(">H", self.__data.read(2)) |
|
388 # unsigned short, big-endian |
|
389 cname = self.__data.read(lenCname) |
|
390 cname = cname.decode("utf-8") |
|
391 |
|
392 value = {} |
|
393 parent[variableName] = ("c_object; cname={0}".format(cname), value) |
|
394 |
|
395 lenVariableName, = struct.unpack(">H", self.__data.read(2)) |
|
396 # unsigned short, big-endian |
|
397 while lenVariableName != 0: |
|
398 variableName = self.__data.read(lenVariableName) |
|
399 variableName = variableName.decode("utf-8") |
|
400 variableType = self.__data.read(1) |
|
401 if variableType == self.Number: |
|
402 self.__parseNumber(variableName, value) |
|
403 elif variableType == self.Boolean: |
|
404 self.__parseBoolean(variableName, value) |
|
405 elif variableType == self.String: |
|
406 self.__parseString(variableName, value) |
|
407 elif variableType == self.ObjObj: |
|
408 self.__parseObject(variableName, value) |
|
409 elif variableType == self.ObjArr: |
|
410 self.__parseArray(variableName, value) |
|
411 elif variableType == self.ObjDate: |
|
412 self.__parseDate(variableName, value) |
|
413 elif variableType == self.ObjXml: |
|
414 self.__parseXml(variableName, value) |
|
415 elif variableType == self.ObjCc: |
|
416 self.__parseOcc(variableName, value) |
|
417 elif variableType == self.ObjM: |
|
418 self.__parseOjm(variableName, value) |
|
419 elif variableType == self.Null: |
|
420 self.__parseNull(variableName, value) |
|
421 elif variableType == self.Undef: |
|
422 self.__parseUndefined(variableName, value) |
|
423 else: |
|
424 raise FlashCookieReaderError( |
|
425 "Unexpected Data Type: " + hex(ord(variableType))) |
|
426 lenVariableName, = struct.unpack(">H", self.__data.read(2)) |
|
427 self.__data.read(1) # '\x09' |