1
|
|
|
#!/usr/bin/env python3 |
2
|
5 |
|
"""This module is used for network connections for TCL tools.""" |
3
|
|
|
|
4
|
5 |
|
import base64 # encoding |
5
|
5 |
|
import binascii # encoding |
6
|
5 |
|
import hashlib # salt |
7
|
5 |
|
import random # salt |
8
|
5 |
|
import time # salt |
9
|
5 |
|
import zlib # encoding |
10
|
|
|
|
11
|
5 |
|
import requests # downloading |
12
|
5 |
|
from bbarchivist import networkutils # network tools |
13
|
5 |
|
from bbarchivist import xmlutilstcl # xml work |
14
|
5 |
|
from bbarchivist.bbconstants import TCLMASTERS # lookup servers |
15
|
|
|
|
16
|
5 |
|
__author__ = "Thurask" |
17
|
5 |
|
__license__ = "WTFPL v2" |
18
|
5 |
|
__copyright__ = "2018 Thurask" |
19
|
|
|
|
20
|
|
|
|
21
|
5 |
|
def tcl_master(): |
22
|
|
|
""" |
23
|
|
|
Get a random master server. |
24
|
|
|
""" |
25
|
5 |
|
return random.choice(TCLMASTERS) |
26
|
|
|
|
27
|
|
|
|
28
|
5 |
|
def tcl_default_id(devid): |
29
|
|
|
""" |
30
|
|
|
Get an IMEI or a serial number or something. |
31
|
|
|
|
32
|
|
|
:param devid: Return default if this is None. |
33
|
|
|
:type devid: str |
34
|
|
|
""" |
35
|
5 |
|
if devid is None: |
36
|
5 |
|
devid = "543212345000000" |
37
|
5 |
|
return devid |
38
|
|
|
|
39
|
|
|
|
40
|
5 |
|
def check_prep(curef, mode=4, fvver="AAA000", cltp=2010, cktp=2, rtd=1, chnl=2, devid=None): |
41
|
|
|
""" |
42
|
|
|
Prepare variables for TCL update check. |
43
|
|
|
|
44
|
|
|
:param curef: PRD of the phone variant to check. |
45
|
|
|
:type curef: str |
46
|
|
|
|
47
|
|
|
:param mode: 4 if downloading autoloaders, 2 if downloading OTA deltas. |
48
|
|
|
:type mode: int |
49
|
|
|
|
50
|
|
|
:param fvver: Initial software version, must be specific if downloading OTA deltas. |
51
|
|
|
:type fvver: str |
52
|
|
|
|
53
|
|
|
:param cltp: 2010 to always show latest version, 10 to show actual updates. Default is 2010. |
54
|
|
|
:type cltp: int |
55
|
|
|
|
56
|
|
|
:param cktp: 2 if checking manually, 1 if checking automatically. Default is 2. |
57
|
|
|
:type cktp: int |
58
|
|
|
|
59
|
|
|
:param rtd: 2 if rooted, 1 if not. Default is 1. |
60
|
|
|
:type rtd: int |
61
|
|
|
|
62
|
|
|
:param chnl: 2 if checking on WiFi, 1 if checking on mobile. Default is 2. |
63
|
|
|
:type chnl: int |
64
|
|
|
|
65
|
|
|
:param devid: Serial number/IMEI. Default is fake, not that it matters. |
66
|
|
|
:type devid: str |
67
|
|
|
""" |
68
|
5 |
|
devid = tcl_default_id(devid) |
69
|
5 |
|
geturl = "http://{0}/check.php".format(tcl_master()) |
70
|
5 |
|
params = {"id": devid, "curef": curef, "fv": fvver, "mode": mode, "type": "Firmware", "cltp": cltp, "cktp": cktp, "rtd": rtd, "chnl": chnl} |
71
|
5 |
|
return geturl, params |
72
|
|
|
|
73
|
|
|
|
74
|
5 |
|
@networkutils.pem_wrapper |
75
|
5 |
|
@networkutils.try_try_again |
76
|
5 |
|
def tcl_check(curef, session=None, mode=4, fvver="AAA000", export=False): |
77
|
|
|
""" |
78
|
|
|
Check TCL server for updates. |
79
|
|
|
|
80
|
|
|
:param curef: PRD of the phone variant to check. |
81
|
|
|
:type curef: str |
82
|
|
|
|
83
|
|
|
:param session: Requests session object, default is created on the fly. |
84
|
|
|
:type session: requests.Session() |
85
|
|
|
|
86
|
|
|
:param mode: 4 if downloading autoloaders, 2 if downloading OTA deltas. |
87
|
|
|
:type mode: int |
88
|
|
|
|
89
|
|
|
:param fvver: Initial software version, must be specific if downloading OTA deltas. |
90
|
|
|
:type fvver: str |
91
|
|
|
|
92
|
|
|
:param export: Whether to export XML response to file. Default is False. |
93
|
|
|
:type export: bool |
94
|
|
|
""" |
95
|
5 |
|
sess = networkutils.generic_session(session) |
96
|
5 |
|
geturl, params = check_prep(curef, mode, fvver) |
97
|
5 |
|
req = sess.get(geturl, params=params) |
98
|
5 |
|
if req.status_code == 200: |
99
|
5 |
|
req.encoding = "utf-8" |
100
|
5 |
|
response = req.text |
101
|
5 |
|
if export: |
102
|
5 |
|
salt = tcl_salt() |
103
|
5 |
|
xmlutilstcl.dump_tcl_xml(response, salt) |
104
|
|
|
else: |
105
|
5 |
|
response = None |
106
|
5 |
|
return response |
107
|
|
|
|
108
|
|
|
|
109
|
5 |
|
def tcl_salt(): |
110
|
|
|
""" |
111
|
|
|
Generate salt value for TCL server tools. |
112
|
|
|
""" |
113
|
5 |
|
millis = round(time.time() * 1000) |
114
|
5 |
|
tail = "{0:06d}".format(random.randint(0, 999999)) |
115
|
5 |
|
return "{0}{1}".format(str(millis), tail) |
116
|
|
|
|
117
|
|
|
|
118
|
5 |
|
def unpack_vdkey(): |
119
|
|
|
""" |
120
|
|
|
Draw the curtain back. |
121
|
|
|
""" |
122
|
5 |
|
vdkey = b"eJwdjwEOwDAIAr8kKFr//7HhmqXp8AIIDrYAgg8byiUXrwRJRXja+d6iNxu0AhUooDCN9rd6rDLxmGIakUVWo3IGCTRWqCAt6X4jGEIUAxgN0eYWnp+LkpHQAg/PsO90ELsy0Npm/n2HbtPndFgGEV31R9OmT4O4nrddjc3Qt6nWscx7e+WRHq5UnOudtjw5skuV09pFhvmqnOEIs4ljPeel1wfLYUF4\n" |
123
|
5 |
|
vdk = zlib.decompress(binascii.a2b_base64(vdkey)) |
124
|
5 |
|
return vdk.decode("utf-8") |
125
|
|
|
|
126
|
|
|
|
127
|
5 |
|
def vkhash(curef, tvver, fwid, salt, mode=4, fvver="AAA000", cltp=2010, devid=None): |
128
|
|
|
""" |
129
|
|
|
Generate hash from TCL update server variables. |
130
|
|
|
|
131
|
|
|
:param curef: PRD of the phone variant to check. |
132
|
|
|
:type curef: str |
133
|
|
|
|
134
|
|
|
:param tvver: Target software version. |
135
|
|
|
:type tvver: str |
136
|
|
|
|
137
|
|
|
:param fwid: Firmware ID for desired download file. |
138
|
|
|
:type fwid: str |
139
|
|
|
|
140
|
|
|
:param salt: Salt hash. |
141
|
|
|
:type salt: str |
142
|
|
|
|
143
|
|
|
:param mode: 4 if downloading autoloaders, 2 if downloading OTA deltas. |
144
|
|
|
:type mode: int |
145
|
|
|
|
146
|
|
|
:param fvver: Initial software version, must be specific if downloading OTA deltas. |
147
|
|
|
:type fvver: str |
148
|
|
|
|
149
|
|
|
:param cltp: 2010 to always show latest version, 10 to show actual updates. Default is 2010. |
150
|
|
|
:type cltp: int |
151
|
|
|
|
152
|
|
|
:param devid: Serial number/IMEI. Default is fake, not that it matters. |
153
|
|
|
:type devid: str |
154
|
|
|
""" |
155
|
5 |
|
vdk = unpack_vdkey() |
156
|
5 |
|
devid = tcl_default_id(devid) |
157
|
5 |
|
query = "id={0}&salt={1}&curef={2}&fv={3}&tv={4}&type={5}&fw_id={6}&mode={7}&cltp={8}{9}".format(devid, salt, curef, fvver, tvver, "Firmware", fwid, mode, cltp, vdk) |
158
|
5 |
|
engine = hashlib.sha1() |
159
|
5 |
|
engine.update(bytes(query, "utf-8")) |
160
|
5 |
|
return engine.hexdigest() |
161
|
|
|
|
162
|
|
|
|
163
|
5 |
|
def download_request_prep(curef, tvver, fwid, salt, vkh, mode=4, fvver="AAA000", cltp=2010, devid=None): |
164
|
|
|
""" |
165
|
|
|
Prepare variables for download server check. |
166
|
|
|
|
167
|
|
|
:param curef: PRD of the phone variant to check. |
168
|
|
|
:type curef: str |
169
|
|
|
|
170
|
|
|
:param tvver: Target software version. |
171
|
|
|
:type tvver: str |
172
|
|
|
|
173
|
|
|
:param fwid: Firmware ID for desired download file. |
174
|
|
|
:type fwid: str |
175
|
|
|
|
176
|
|
|
:param salt: Salt hash. |
177
|
|
|
:type salt: str |
178
|
|
|
|
179
|
|
|
:param vkh: VDKey-based hash. |
180
|
|
|
:type vkh: str |
181
|
|
|
|
182
|
|
|
:param mode: 4 if downloading autoloaders, 2 if downloading OTA deltas. |
183
|
|
|
:type mode: int |
184
|
|
|
|
185
|
|
|
:param fvver: Initial software version, must be specific if downloading OTA deltas. |
186
|
|
|
:type fvver: str |
187
|
|
|
|
188
|
|
|
:param cltp: 2010 to always show latest version, 10 to show actual updates. Default is 2010. |
189
|
|
|
:type cltp: int |
190
|
|
|
|
191
|
|
|
:param devid: Serial number/IMEI. Default is fake, not that it matters. |
192
|
|
|
:type devid: str |
193
|
|
|
""" |
194
|
5 |
|
devid = tcl_default_id(devid) |
195
|
5 |
|
posturl = "http://{0}/download_request.php".format(tcl_master()) |
196
|
5 |
|
params = {"id": devid, "curef": curef, "fv": fvver, "mode": mode, "type": "Firmware", "tv": tvver, "fw_id": fwid, "salt": salt, "vk": vkh, "cltp": cltp} |
197
|
5 |
|
if mode == 4: |
198
|
5 |
|
params["foot"] = 1 |
199
|
5 |
|
return posturl, params |
200
|
|
|
|
201
|
|
|
|
202
|
5 |
|
@networkutils.pem_wrapper |
203
|
5 |
|
@networkutils.try_try_again |
204
|
5 |
|
def tcl_download_request(curef, tvver, fwid, salt, vkh, session=None, mode=4, fvver="AAA000", export=False): |
205
|
|
|
""" |
206
|
|
|
Check TCL server for download URLs. |
207
|
|
|
|
208
|
|
|
:param curef: PRD of the phone variant to check. |
209
|
|
|
:type curef: str |
210
|
|
|
|
211
|
|
|
:param tvver: Target software version. |
212
|
|
|
:type tvver: str |
213
|
|
|
|
214
|
|
|
:param fwid: Firmware ID for desired download file. |
215
|
|
|
:type fwid: str |
216
|
|
|
|
217
|
|
|
:param salt: Salt hash. |
218
|
|
|
:type salt: str |
219
|
|
|
|
220
|
|
|
:param vkh: VDKey-based hash. |
221
|
|
|
:type vkh: str |
222
|
|
|
|
223
|
|
|
:param session: Requests session object, default is created on the fly. |
224
|
|
|
:type session: requests.Session() |
225
|
|
|
|
226
|
|
|
:param mode: 4 if downloading autoloaders, 2 if downloading OTA deltas. |
227
|
|
|
:type mode: int |
228
|
|
|
|
229
|
|
|
:param fvver: Initial software version, must be specific if downloading OTA deltas. |
230
|
|
|
:type fvver: str |
231
|
|
|
|
232
|
|
|
:param export: Whether to export XML response to file. Default is False. |
233
|
|
|
:type export: bool |
234
|
|
|
""" |
235
|
5 |
|
sess = networkutils.generic_session(session) |
236
|
5 |
|
posturl, params = download_request_prep(curef, tvver, fwid, salt, vkh, mode, fvver) |
237
|
5 |
|
req = sess.post(posturl, data=params) |
238
|
5 |
|
if req.status_code == 200: |
239
|
5 |
|
req.encoding = "utf-8" |
240
|
5 |
|
response = req.text |
241
|
5 |
|
if export: |
242
|
5 |
|
xmlutilstcl.dump_tcl_xml(response, salt) |
243
|
|
|
else: |
244
|
5 |
|
response = None |
245
|
5 |
|
return response |
246
|
|
|
|
247
|
|
|
|
248
|
5 |
|
def encrypt_header_prep(address, encslave): |
249
|
|
|
""" |
250
|
|
|
Prepare variables for encrypted header check. |
251
|
|
|
|
252
|
|
|
:param address: File URL minus host. |
253
|
|
|
:type address: str |
254
|
|
|
|
255
|
|
|
:param encslave: Server hosting header script. |
256
|
|
|
:type encslave: str |
257
|
|
|
""" |
258
|
5 |
|
encs = {b"YWNjb3VudA==" : b"emhlbmdodWEuZ2Fv", b"cGFzc3dvcmQ=": b"cWFydUQ0b2s="} |
259
|
5 |
|
params = {base64.b64decode(key): base64.b64decode(val) for key, val in encs.items()} |
260
|
5 |
|
params[b"address"] = bytes(address, "utf-8") |
261
|
5 |
|
posturl = "http://{0}/encrypt_header.php".format(encslave) |
262
|
5 |
|
return posturl, params |
263
|
|
|
|
264
|
|
|
|
265
|
5 |
|
@networkutils.pem_wrapper |
266
|
5 |
|
def encrypt_header(address, encslave, session=None): |
267
|
|
|
""" |
268
|
|
|
Check encrypted header. |
269
|
|
|
|
270
|
|
|
:param address: File URL minus host. |
271
|
|
|
:type address: str |
272
|
|
|
|
273
|
|
|
:param encslave: Server hosting header script. |
274
|
|
|
:type encslave: str |
275
|
|
|
|
276
|
|
|
:param session: Requests session object, default is created on the fly. |
277
|
|
|
:type session: requests.Session() |
278
|
|
|
""" |
279
|
5 |
|
sess = networkutils.generic_session(session) |
280
|
5 |
|
posturl, params = encrypt_header_prep(address, encslave) |
281
|
5 |
|
req = sess.post(posturl, data=params) |
282
|
5 |
|
if req.status_code == 206: # partial |
283
|
5 |
|
contentlength = int(req.headers["Content-Length"]) |
284
|
5 |
|
sentinel = "HEADER FOUND" if contentlength == 4194320 else "NO HEADER FOUND" |
285
|
|
|
else: |
286
|
5 |
|
sentinel = None |
287
|
5 |
|
return sentinel |
288
|
|
|
|
289
|
|
|
|
290
|
5 |
|
@networkutils.pem_wrapper |
291
|
|
|
def remote_prd_info(): |
292
|
|
|
""" |
293
|
|
|
Get list of remote OTA versions. |
294
|
|
|
""" |
295
|
5 |
|
dburl = "https://tclota.birth-online.de/json_lastupdates.php" |
296
|
5 |
|
req = requests.get(dburl) |
297
|
5 |
|
reqj = req.json() |
298
|
5 |
|
otadict = {val["curef"]: val["last_ota"] for val in reqj.values() if val["last_ota"] is not None} |
299
|
|
|
return otadict |
300
|
|
|
|